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小时内与您取得联系。