这篇文章发布于 2017年08月4日,星期五,23:59,归类于 SVG相关。 阅读 79637 次, 今日 11 次 34 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=6326
本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。
一、从SVG文本换行说起
SVG和CSS几乎可以看成是同一个年代出来的东西,但是在Web界面展示这块,CSS长期统领江山,SVG偃旗息鼓,直到这些年,SVG开始乘势而上。
时势造英雄。当年web网页都是以图文展示为主,所以门户,博客这些网站兴起。而SVG擅长的是图形展示,对于文本呈现,只能嘿嘿一下,跟CSS相比那可就弱了几条街。举个最简单的效果,文本换行。在CSS下,当我们一段文字很长的时候,是会自动换到下一行的,但是在SVG中,基本上就是要手动控制换行,对于新闻、博客展示而言简直就是噩梦。于是,CSS火了,SVG萎了。
但,时代是发展的,现代网络带宽有大腿粗,电脑性能比第一次还快,屏幕密度比姚明还高,丰富的富媒体展现和复杂的图形表现需求越来越高,随着flash的日渐衰微,显然,规范标准化的SVG就顺势崛起。
然而需求是千变万化的,当我们使用SVG实现一些视觉表现精湛的效果的时候,免不了会有增加一段描述文字的需求。呵呵,这个时候SVG怕是要傻眼了,SVG的<text>
元素应付多行文字实在不擅长,如果SVG中的文字可以如同CSS中的表现一样就好了?
嘿,还真有,那就是SVG的<foreignObject>
元素。
二、SVG forginObject元素简介
我们平常使用SVG使用的是SVG的命名空间:
<svg xmlns="http://www.w3.org/2000/svg"></svg>
xmlns
全称是“XML Namespaces”,指“XML命名空间”。XML是一个比较大的统称,是一种用于标记电子文件使其具有结构性的标记语言。我们平时写的HTML代码是XML,RSS订阅文档也是种XML,SVG也属于XML。而这些XML类型的渲染规则是有差异的,例如HTML中裸露的<circle>
元素是不会表现为圆,而是一个普通的自定义元素。RSS中的XML浏览器会自动进行文章排版渲染等。
所以我们可以看到,指定命名空间可以让浏览器精准解析,举个例子,如下XML代码:
<svg width="12" height="12" viewBox="0 0 12 12"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg>
虽然是SVG标签,明摆着应该安装SVG渲染,但是,实际上,由于没有指定xmlns
,在浏览器中打开的时候,直接以普通XML文档树渲染了。
可狠狠的点击此SVG文件亲自感受下:close-no-ns.svg
Chrome浏览器下:
Firefox浏览器下:
一旦我们加上了SVG专属命名空间,如下代码:
<svg width="12" height="12" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg"><path d="M10.263 10.874L6 6.61l-4.264 4.264a.431.431 0 0 1-.61-.61L5.39 6.001 1.128 1.736a.431.431 0 0 1 .61-.61L6 5.391l4.264-4.263a.431.431 0 0 1 .61.61L6.61 6l4.262 4.264a.43.43 0 1 1-.61.609z" stroke="#979797" fill="#999"/></svg>
则符合预期的SVG图形就渲染出来了。
可狠狠的点击此SVG文件亲自感受下:close.svg
结果如下截图:
这就是命名空间的作用。但这里有必要额外说明下:如果我们的SVG文件是直接内联在XHTML页面中,是可以不指定命名空间的!
好,回到<foreignObject>
这里,<foreignObject>
元素的作用是可以在其中使用具有其它XML命名空间的XML元素,换句话说借助<foreignObject>
标签,我们可以直接在SVG内部嵌入XHTML元素,举个简单的例子:
<svg xmlns="http://www.w3.org/2000/svg"> <foreignObject width="120" height="50"> <body xmlns="http://www.w3.org/1999/xhtml"> <p>文字。</p> </body> </foreignObject> </svg>
可以看到<foreignObject>
标签里面有一个设置了xmlns="http://www.w3.org/1999/xhtml"
命名空间的<body>
标签,此时<body>
标签及其子标签都会按照XHTML标准渲染,实现了SVG和XHTML的混合使用。
这种混合特性有什么作用呢?作用很多,其中之一就是轻松实现SVG内的文本自动换行。
三、SVG forginObject元素与文本自动换行
SVG要实现文本换行,往往需要手动阻断,类似下面的代码:
<svg xmlns="http://www.w3.org/2000/svg"> <text font-size="12"> <tspan x="0" y="10">一段需要word wrap</tspan> <tspan x="0" y="26">的文字。</tspan> </text> </svg>
需要2
个<tspan>
元素,这一点都不工程。虽然Chrome浏览器可以对<text>
标签进行white-space:normal
的强制设置,但也只是Chrome浏览器可以。
但是如果使用<foreignObject>
元素,则自动换行就是小菜:
<svg xmlns="http://www.w3.org/2000/svg"> <foreignObject width="120" height="50"> <body xmlns="http://www.w3.org/1999/xhtml"> <p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p> </body> </foreignObject> </svg>
结果Chrome浏览器下效果为:
您可以狠狠地点击这里:foreignObject下文本自动换行demo
四、SVG forginObject元素其它作用-图片生成
除了轻松实现文本换行,SVG <foreignObject>
元素还有其他更高级的应用,就是可以将页面上的DOM元素轻松变成图片,原理如下:
- 获取对应DOM元素的
outerHTML
代码; - 放在
<foreignObject>
元素中; - 图片方式显示我们的SVG图形,例如:
<img width="300" height="150" src='data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"><foreignObject width="120" height="50"><body xmlns="http://www.w3.org/1999/xhtml"><p style="font-size:12px;margin:0;">一段需要word wrap的文字。</p></body></foreignObject></svg>'>
- 上一步的图片本质还是SVG,我们可以借助
canvas
drawImage()
方法将图片放在画布上,然后使用canvas.toDataURL()
方法转换成png
或jpg
图片,核心代码:var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.drawImage(img, 0, 0); img.src = canvas.toDataURL('image/png');
上一篇文章“canvas实现图片前端JS压缩”有详细的canvas绘图和转图片示意,有兴趣可以参考下。
一旦我们可以把DOM元素转换成图片,我们就可以轻轻松松配合JS在前端实现网页截图功能。
想了想,还是做个demo示意下吧,您可以狠狠地点击这里:借助forginObject实现DOM转图片并直接下载demo
点击demo页面右侧小模块任意区域,都可以保存想要内容,例如,外面框框:
结果:
触发保存,保存后的图片(点击可预览)就是DOM页面的样子。
更新于2020-06-18
回过头看此文,发现demo页面生成图片的功能挂了,一排查,发现Chrome浏览器提高了安全限制级别,内联CSS样式中的#
字符也需要转义,也是demo页面源码调整了下,现在功能又OK啦!
——-更新end——-
补充说明:理论上,借助<foreignObject>
实现截图效果Firefox也是支持的,但是,上面例子为了简化,直接把页面上的<style>
元素以及里面CSS代码一起内联到SVG的<foreignObject>
中了,这种用法只有Chrome浏览器支持。因此,上面demo效果仅在Chrome及其内核浏览器下有效。Firefox浏览器要想支持,需要使用特殊方法将所有元素样式style
属性内联在元素上。
SVG forginObject兼容性
可以看出SVG的<foreignObject>
的特性非常棒!但相比其他SVG元素兼容性要差一些,主要是IE浏览器,好在大头Chrome和Firefox都支持,Safari虽然支持<foreignObject>
貌似由于安全限制,转换成图片功能受限。
五、结束语
SVG <foreignObject>
元素可以让DOM变图片,发挥我们的创造力,我们可以做的事情就非常非常多,举个例子,各种特效,因为DOM图片化,我们就能使用canvas
读取图片完整的像素点信息,通过特点的算法,我们就可以做很多效果,例如常见的高斯模糊。高斯模糊浏览器天然支持,那我们可以实现浏览器默认没有的动感模糊或者镜像模糊;又或者我们可以把DOM元素变成粒子特效(canvas擅长的);又或者实现类似OS X系统的DOCK中软件打开和收起的拉伸扭曲效果等等。
有技术,有创意,还不赶快行动,把你的网页炫起来!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:https://www.zhangxinxu.com/wordpress/?p=6326
(本篇完)
- 使用jsPDF导出PDF文件实践分享 (0.430)
- CSS滤镜和混合模式处理的图片如何上传下载? (0.402)
- 小tips:使用canvas在前端实现图片水印合成 (0.229)
- 做了个纯前端JPG/PNG尺寸缩放+压缩的在线工具 (0.221)
- 图片动态局部毛玻璃模糊效果的实现 (0.182)
- canvas文本绘制自动换行、字间距、竖排等实现 (0.181)
- 纯CSS图片滤镜项目CSSgram简介 (0.166)
- 瞎折腾:把JS,CSS任意文本文件加密成一张图片 (0.153)
- JS检测PNG图片是否有透明背景、抠图等相关处理 (0.138)
- canvas实现任意字符图形的打点或连线动画 (0.107)
- 解决canvas图片getImageData,toDataURL跨域问题 (RANDOM - 0.015)
Safari虽然支持貌似由于安全限制,转换成图片功能受限。
——-
这个有办法处理吗。我尝试用ios微信浏览器打开demo地址https://www.zhangxinxu.com/study/201708/svg-foreignobject-dom-to-image.html 点击的时候,第一次图片出不来,第二次正常。我自己项目也是这样,时好时不好的
以这种方式生成的svg无法在ps中使用,ps,sketch中都无法正常打开
查了好多资料,都不太明白命名空间的作用,因为就算没加命名空间,现在在大部分浏览器下,html中的svg标签显示好像也不会有什么问题。都可以正常显示。在老师这里稍稍明白了一些。原来要用.svg文件才能看出区别。
保存图片的demo,并不能运行,我发现是foreignObject 中style中不能使用#f0f0f0这种格式的颜色而是使用green这种格式?
标签为什么会使gif动图在点击前不动呢?
Demo的img.onload触发不了
你好 我用svg封装的d3画地图的时候 有一个功能是根据时间切换私图的背景色 所以切换时间的时候会重新渲染地图 但是在数据正常元素也拿到的情况下 第一次渲染地图背景色正常 切换时间之后firefox和safari的attr.attr(‘fill’, value)就没有变化 chrome是正常的 这是为什么 firefox和safari有时好时不好的情况 就是在第一次渲染正常的情况下后面的渲染没有根据实际值重新渲染成功
toSvg方法得到svg串后,再使用toDataUrl在安卓微信中会报错。原因是得到的svg串中含有foreignObject。使用toJpeg方法也会报同样的错。请问有什么办法解决
这个支持css3属性么 上传图片后做滤镜处理 保存滤镜之后的图片 ; html2canvas不支持css3属性
foreignObject的宽高默认是auto,但是auto的话,就算有内容宽高也居然是0。。。。请问可否让他自动适应内容的宽高?
这里我的想法是可以根据foreignObject内部DOM元素宽高计算得出foreignObject宽高以达到自适应目的,而且也灵活
使用foreignObject换行时, 我是通过js动态的向p标签下添加文本,结果添加的文本在页面上没有显示。我的添加代码是:
var svg = d3.select(“svg”);
svg.append(“switch”)
.append(“foreignObject”)
.attr(“x”, 20)
.attr(“y”, 0)
.attr(“width”, 150)
.attr(“height”, 200)
.attr(“style”, “outline: 1px solid blue;”)
.append(“body”)
.append(“p”)
.attr(“xmlns”, “http://www.w3.org/1999/xhtml”)
.text(“Text goes here, Text goes here, Text goes here, Text goes here”);
把您的代码抓下来尝试了一下,图片是本地的,但是一直报这个错“Tainted canvases may not be exported.”,搞不懂是怎么回事???
看了您的文章,手动操作后遇到了一个问题,就是svg转成img后字体的大小不对,比如我在html里设置了字体的大小为60px.转成svg后市正常的,但是由svg转成img后,img的字体大小就变成了默认的了……总觉得和svg有问题,但找不到问题的所在,求大佬帮忙看看
看了你的文章实在很精彩,在截取的时候遇到两个问题,1、遇到视频标签出了点问题,2、遇到滚动文本只有样式没有内容。….。 觉得和样式有点关系,求助大神。
恕我愚钝,没明白你所说的具体是个什么问题。
已经安装了dom-to-image插件 ,想问这是什么原因引起的呢
根据这篇教程,写了一个html2img的插件,中间遇到一个问题,html中包含svg,并且svg中包含xlink:href=”#id”,就无法到处图片,关键再这个井号,去掉井号就可以,但是去掉井号,明显这句引用就失效了,不知道大大有什么解决办法。我搜了一晚上,都没找到,现在已经凌晨了。
测试代码:html:
<svg viewBox=”0 0 100 100″>
<path d=”M 0,50 a 50,50 0 1,1 0,1 z” id=”circle”/>
<text>
<textPath xlink:href=”#circle”>test</textPath>
</text>
</svg>
会不会是id重复或者其他什么原因啊。
尝试了一下
1.必须使用内联样式;
2.rem不生效;
3.元素的背景图片获取不到
请问foreignObject元素的真实宽高是否有办法获取到手
请问下svg上添加的foreignObject如何设置坐标,foreignObject的坐标我用x y设置之后,g元素的坐标就被干掉了
demo的方法css样式是不是会被清掉
foreignObject 这玩意不晓得能不能直接引入link css ,
整了半天样式就是出不来,换了style的方式果然是可以。。唉
我也是,样式表没有了、、、只有内联样式可以存在,有解决方案么?~
var bodyE = document.querySelector(‘body’)
domToImg(bodyE)
如何用foreignObject实现类似iOS那样的毛玻璃效果?
这个实验进行的怎么样啦
请问一下,”Firefox浏览器要想支持,需要使用特殊方法将所有元素样式style属性内联在元素上。”这里的特殊方法是指什么方法?还有‘内联在元素上’,这个元素是什么元素?
生命不息,折腾不止
怎么把rss订阅设置为摘要了?这样不友好。
svg绘制的二维图形可以按照以上方式截图保存么?
自然可以!
比第一次还快,嘿嘿嘿,是我想太多吗