自由拖拽缩放的节点 —— react-rnd
说到拖拽,第一反应当然是监听鼠标事件来修改 dom
元素的位置,而缩放的话,则是在对元素边界进行操作时重新修正元素的 position
和 width
、height
属性。可以参考下 react-rnd 这个插件,它引用了 react-draggable 和 react-resizeable 两个插件来实现元素的拖拽和缩放。
此时理论上已经实现了 dom
元素的拖拽和缩放,但是在添加了 svg
图片之后很快发现,由于外层的拖拽是由 document.addEventListener
来实现的,但是在 mouseDown
的时候,鼠标点击的位置实际上是 svg
内的 document
。
那么这个时候应该怎么处理呢?最简单的思路:把 svg
的 document
取出来,放到父级的 document
里面。
自由的 svg —— react-svg
有了把 svg
的 document
取出来的思路,很容易能找到一个插件:react-svg,它的实现思路与上面提到的完全一致,此处贴上它的核心代码供查看:
此时发现,添加的 svg
图片已经可以自由的拖拽和缩放了。另外由于 react-svg 还开放了一个属性 beforeInjection
,可以通过这个属性来在 svg
挂载之前修改它的属性,如 stroke
、stroke-width
、fill
等,因此可以更加灵活得处理 svg
图片。
svg
的缩放 —— preserveAspectRatio
、vector-effect
很快又发现一个问题,svg
的缩放默认是等比的,也就是说当横向拉长图片的时候,它并不会变大,只会横向偏移居中。
这个时候就要用到 svg
自带的一个属性:preserveAspectRatio
,用来表示是否强制进行统一缩放,当设置为 none
的时候,svg
图片不会进行强制统一缩放,如果需要,会缩放指定元素的图形内容,使元素的边界完全匹配视图矩形。如下图。其他类型强制缩放可参考 MDN。
现在 svg
终于可以自由缩放了,但是很快又发现,把一个 svg
放大之后,它的线条宽度也跟着变宽了,那么有没有什么属性来保持线条宽度不变呢?答案是有的,vector-effect
,但是这个属性只对部分 svg
元素( <circle>
,<ellipse>
,<foreignObject>
,<image>
,<line>
,<path>
,<polygon>
,<polyline>
,<rect>
,<text>
,<textPath>
,<tspan>
,<use>
)生效,而它的属性有以下几个:none
该值指定不应用矢量效果,即,使用默认的渲染行为,即首先用指定的绘画填充形状的几何形状,然后使用指定的绘画描边轮廓。non-scaling-stroke
该值修改了笔触的方式。通常,笔触涉及在当前用户坐标系中计算形状路径的笔触轮廓,并用笔触颜料(颜色或渐变)填充轮廓。该值的最终视觉效果是笔触宽度不依赖于元素的变换(包括非均匀缩放和剪切变换)和缩放级别。non-scaling-size
该值指定元素及其后代使用的特殊用户坐标系。尽管从宿主坐标空间进行任何转换更改,该用户坐标系的比例也不会更改。但是,它没有指定抑制旋转和偏斜。同样,它也不指定用户坐标系的原点。由于此值抑制了用户坐标系的缩放,因此它还具有 non-scaling-stroke
的特性。non-rotation
该值指定元素及其后代使用的特殊用户坐标系。尽管从宿主坐标空间发生任何变换更改,该用户坐标系的旋转和倾斜仍被抑制。但是,它没有指定抑制缩放。同样,它也没有指定用户坐标系的原点。fixed-position
该值指定元素及其后代使用的特殊用户坐标系。尽管从宿主坐标空间进行任何转换更改,用户坐标系的位置都是固定的。但是,它没有指定抑制旋转,偏斜和缩放。当同时指定了该矢量效果和 transform
属性, transform
属性将因该矢量效果而被消耗。
当设置vector-effect="non-scaling-stroke"
后,svg
终于看起来正常了~
尾声
以上就是在给页面上添加一个可以自由拖拽、缩放、编辑颜色的 svg
图片时总结的一些东西。另外还有一个没有解决的问题,如果path
的内容是通过类似同心圆的方式来绘制图形的时候,并没有什么好的方法来保证缩放时候线条宽度的变化。