dom-to-image是一個js庫,可以將任意dom節點轉換為矢量(SVG)或光柵(PNG或JPEG)圖像。
npm install dom-to-image -S
/* in ES 6 */
import domtoimage from 'dom-to-image';
/* in ES 5 */
var domtoimage=require('dom-to-image');
所有高階函數都接受DOM節點和渲染選項options ,并返回promises。
<div id="my-node"></div>
var node=document.getElementById('my-node');
// options 可不傳
var options={}
domtoimage.toPng(node, options)
.then(function (dataUrl) {
var img=new Image();
img.src=dataUrl;
document.body.appendChild(img);
})
.catch(function (error) {
console.error('oops, something went wrong!', error);
});
domtoimage.toBlob(document.getElementById('my-node'))
.then(function (blob) {
console.log('blob', blob)
});
domtoimage.toJpeg(document.getElementById('my-node'), { quality: 0.95 })
.then(function (dataUrl) {
var link=document.createElement('a');
link.download='my-image-name.jpeg';
link.href=dataUrl;
link.click();
});
function filter (node) {
return (node.tagName !=='i');
}
domtoimage.toSvg(document.getElementById('my-node'), {filter: filter})
.then(function (dataUrl) {
/* do something */
});
var node=document.getElementById('my-node');
domtoimage.toPixelData(node)
.then(function (pixels) {
for (var y=0; y < node.scrollHeight; ++y) {
for (var x=0; x < node.scrollWidth; ++x) {
pixelAtXYOffset=(4 * y * node.scrollHeight) + (4 * x);
/* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
pixelAtXY=pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
}
}
});
Name | 類型 | Default | Description |
filter | Function | —— | 以DOM節點為參數的函數。如果傳遞的節點應包含在輸出中,則應返回true(排除節點意味著也排除其子節點) |
bgcolor | String | —— | 背景色的字符串值,任何有效的CSS顏色值。 |
height | Number | —— | 渲染前應用于節點的高度(以像素為單位)。 |
width | Number | —— | 渲染前應用于節點的寬度(以像素為單位)。 |
style | Object | —— | object對象,其屬性在渲染之前要復制到節點的樣式中。 |
quality | Number | 1.0 | 介于0和1之間的數字,表示JPEG圖像的圖像質量(例如0.92=>92%)。默認值為1.0(100%) |
cacheBust | Boolean | false | 設置為true可將當前時間作為查詢字符串附加到URL請求以啟用清除緩存。 |
imagePlaceholder | Boolean | undefined | 獲取圖片失敗時使用圖片的數據URL作為占位符。默認為未定義,并將在失敗的圖像上引發錯誤。 |
dom-to-image使用SVG的一個特性,它允許在標記中包含任意HTML內容。
dom-to-image.js
// Default impl options
var defaultOptions={
// Default is to fail on error, no placeholder
imagePlaceholder: undefined,
// Default cache bust is false, it will use the cache
cacheBust: false
};
var domtoimage={
toSvg: toSvg,
toPng: toPng,
toJpeg: toJpeg,
toBlob: toBlob,
toPixelData: toPixelData,
impl: {
fontFaces: fontFaces,
images: images,
util: util,
inliner: inliner,
options: {}
}
};
if (typeof module !=='undefined')
module.exports=domtoimage;
else
global.domtoimage=domtoimage;
function toJpeg(node, options) {
options=options || {};
return draw(node, options)
.then(function (canvas) {
return canvas.toDataURL('image/jpeg', options.quality || 1.0);
});
}
復制代碼
function draw(domNode, options) {
return toSvg(domNode, options)
.then(util.makeImage)
.then(util.delay(100))
.then(function (image) {
var canvas=newCanvas(domNode);
canvas.getContext('2d').drawImage(image, 0, 0);
return canvas;
});
function newCanvas(domNode) {
var canvas=document.createElement('canvas');
canvas.width=options.width || util.width(domNode);
canvas.height=options.height || util.height(domNode);
if (options.bgcolor) {
var ctx=canvas.getContext('2d');
ctx.fillStyle=options.bgcolor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
return canvas;
}
}
function toSvg(node, options) {
options=options || {};
copyOptions(options);
return Promise.resolve(node)
.then(function (node) {
return cloneNode(node, options.filter, true);
})
.then(embedFonts)
.then(inlineImages)
.then(applyOptions)
.then(function (clone) {
return makeSvgDataUri(clone,
options.width || util.width(node),
options.height || util.height(node)
);
});
function applyOptions(clone) {
if (options.bgcolor) clone.style.backgroundColor=options.bgcolor;
if (options.width) clone.style.width=options.width + 'px';
if (options.height) clone.style.height=options.height + 'px';
if (options.style)
Object.keys(options.style).forEach(function (property) {
clone.style[property]=options.style[property];
});
return clone;
}
}
作者:知其
https://juejin.cn/post/6988045156473634852
家好,我是開源探索者,持續分享開源項目,關注技術的最新動態,分享自己的經驗和見解。
大家好,我是開源探索者。
今天給大家介紹一個非常牛的開源項目:Screenshot-to-code。
Screenshot-to-code 是一個可以將屏幕截圖轉化為 HTML/JS/Tailwind CSS 代碼的工具。它利用 GPT-4 Vision 生成代碼,結合 DALL-E 3 生成相似的圖片。
能夠將屏幕截圖瞬間轉變為可運行的代碼。這意味著,你只需要截取一個網頁或應用程序的截圖,Screenshot-to-code 就可以自動生成對應的 HTML、CSS、JavaScript 代碼。
這項功能對于初學者來說非常友好,可以幫助快速學習前端開發。對于經驗豐富的開發人員來說,也可以節省大量的時間和精力。
項目利用最新的 GPT-4 Vision 技術,可以生成高度智能化的代碼,能夠幫助我們更好地理解屏幕截圖中的元素,并生成更加貼近設計意圖的代碼。
可以結合 DALL-E 3 技術生成相似的圖片,我們可以使用 Screenshot-to-code 生成一個網頁或應用程序的截圖,然后使用 DALL-E 3 生成一個相似的圖片。
這項功能可以讓我們的頁面呈現更加豐富多彩、獨具特色。
一個例子
Screenshot-to-code 的使用很簡單,官方給了很詳細的說明。
使用前提是有一個能夠訪問 GPT-4 Vision API 的 OpenAI API 密鑰。
接著按照下面的步驟:
1、下載 Screenshot-to-code 的源代碼。
2、在 backend/.env 文件中添加你的 OpenAI API 密鑰。
3、使用 poetry install 安裝依賴項。
4、使用 poetry run uvicorn main:app --reload --port 700運行后端。(如果您希望在不同端口上運行后端,可以修改文件 VITE_WS_BACKEND_URLfrontend/.env.local)
5、使用 yarn 安裝前端依賴項。
6、使用 yarn dev 運行前端。
7、打開瀏覽器,訪問 http://localhost:5173 即可使用。
如果你安裝了Docker,也可以用下面的命令快速開始:
echo "OPENAI_API_KEY=sk-your-key" > .env
docker-compose up -d --build
當然,如果你也不想這么麻煩,官方提供了一個在線的版本供體驗使用
https://screenshottocode.com
目前 Screenshot-to-code 項目依然還在開發更新中,已經取得了令人印象深刻的進展。未來,Screenshot-to-code 會在支持更多的語言和框架、提高生成代碼的準確性和效率、增加更多功能,例如代碼片段共享和代碼編輯器集成等方面進行提示。
開源君有一種感覺,Screenshot-to-code 有可能會成為未來前端開發的必備工具。
關于項目的更多細節,感興趣的同學可以自行去項目地址查看。
項目地址:
https://github.com/abi/screenshot-to-code
在數字時代的浪潮中,有一群人他們不畏艱難,勇攀技術高峰,他們就是開源探索者。他們不僅僅是技術的實踐者,更是開源文化的傳播者和推動者。
在開源的世界里,沒有絕對的權威,只有共同的合作。
文轉自測試人社區,原文鏈接:selenium ---- 異常自動截圖 - 學習筆記 - 測試人社區
# 目標1:實現代碼異常的時候,截圖/打印page_source
# 實現方法:try/catch 配合截圖/ page_source操作
import time
import allure
from selenium import webdriver
from selenium.webdriver.common.by import By
#====問題1:異常處理會影響用例本身的結果(本該執行失敗的用例,由于捕獲了異常,導致執行成功)
# 解決方案: 在exception之后再把異常拋出(raise Exception)
#====問題2:異常捕獲處理代碼和業務代碼無關,不能耦合
# 解決方案:使用裝飾器裝飾用例或者相關方法封裝異常捕獲處理代碼
# 1、先把裝飾器的架子搭好
# 2、把相關邏輯嵌套進來
#====問題3: 被裝飾的函數test_baidu()還沒執行, 就先調用了裝飾器ui_exception_record,此時還沒有self.driver,導致args[0]還獲取不到self
# 解決方案:獲取self.driver的操作,放在裝飾器中被裝飾的函數之后
#====問題4:隱藏的小bug,一旦被裝飾方法有返回值,會丟失返回值
# 解決方案:給被裝飾器裝飾的方法添加return返回值
def ui_exception_record(func):
def inner(*args, **kwargs):
"""
內函數,實現封裝異常捕獲處理的代碼
:param args: 不定長參數,是個元組,args[0] 是 TestBaidu類的實例對象,相當于self
:param kwargs:
:return:
"""
# 獲取TestBaidu類中被裝飾方法的self,也就是實例對象,args[0]相當于self,args[0].driver=self.driver
# 前提條件: TestBaidu類中的driver變量是一個實例變量 self.driver
try:
# 當被裝飾方法/函數發生異常,就捕獲并做數據記錄
func(*args, **kwargs)
except Exception:
#====問題3: 被裝飾的函數test_baidu()還沒執行, 就先調用了裝飾器ui_exception_record,此時還沒有self.driver,導致args[0]還獲取不到self
# 解決方案一:獲取self.driver的操作,放在裝飾器中被裝飾的函數func(*args, **kwargs)之后
driver=args[0].driver
# 出現異常情況
# print("出現異常情況")
# 截圖操作
timestamp=int(time.time())
image_path=f"../images/image_{timestamp}.PNG"
driver.save_screenshot(image_path)
# page_source操作
page_source_path=f"../page_source/page_source_{timestamp}.HTML"
with open(page_source_path, "w", encoding="utf8") as f:
f.write(driver.page_source)
# 將截圖放到allure報告中
allure.attach.file(image_path, name="picture", attachment_type=allure.attachment_type.PNG)
# 將page_source放到allure報告中
# 如果想要HTML格式展示在報告中,則使用HTML
allure.attach.file(page_source_path, name="page_source", attachment_type=allure.attachment_type.HTML)
# 如果想要TEXT格式展示在報告中,則使用TEXT
allure.attach.file(page_source_path, name="page_source", attachment_type=allure.attachment_type.HTML)
# 待截圖和page_source數據保存完成后,拋出異常,此時用例會按正常情況,執行失敗
raise Exception
# 返回內函數對象
return inner
class TestBaidu:
def setup_class(self):
# 解決方案二:在前置條件中對self.driver進行實例化操作
self.driver=webdriver.Chrome()
@ui_exception_record
def find(self):
return self.driver.find_element(By.ID, "su1")
def test_baidu(self):
self.driver.get("https://www.baidu.com")
self.find().click()
# self.driver.find_element(By.ID, "su1")
*請認真填寫需求信息,我們會在24小時內與您取得聯系。