变形组件
可以对目标元素进行类似ps的操作如:拖动,旋转,放大缩小
场景
自定义海报排版,自定义宣传页面
参考:做单页http://www.zuodanye.com/danyeeditor?tid=WTQX3VG2&event_uid=949811
我的思路
- 利用mousedown和mousemove和mouseup事件来实现拖拽
- 在拖拽基础上,实现基于绝对定位的选中,移动,旋转,伸缩四个动作
选中实现
发生在当前选中元素mousedown阶段,但是在document.documentElement的mousedown事件后
this.lastMouseX = e.pageX || e.clientX + document.documentElement.scrollLeft; // 鼠标位置
this.lastMouseY = e.pageY || e.clientY + document.documentElement.scrollTop;
- 事件绑定在了元素本身以及document.documentElement上,并用捕获的方式从外到内的顺序,好处是:可以提前知道是否点击了当前元素,contains判断是否为当前的元素,并且判断接下来是移动|旋转|伸缩其中的哪个操作,记录好鼠标开始位置top和left
移动操作
发生在顶级元素mousemove阶段
1. 计算当前鼠标位置和上一次鼠标位置的差值,直接赋予目前元素:
// 鼠标移动后X值
const mouseX = e.pageX || e.clientX + document.documentElement.scrollLeft;
const mouseY = e.pageY || e.clientY + document.documentElement.scrollTop;
// 得出鼠标移动变化的位置值
let diffX = mouseX - this.lastMouseX + this.mouseOffX
let diffY = mouseY - this.lastMouseY + this.mouseOffY
this.elmX += diffX;
this.elmY += diffY;
新的top = 当前(旧的)top+差值top
新的left = 当前(旧的)left+差值left
旋转操作
发生在顶级元素mousemove阶段
1. 利用Math.atan2方法计算出旧的鼠标位置点到中心点的斜率(先拿到弧度值,再配合PI算出旧的弧度
const origin = this.getOrigin(); // 获取中心点
const lastAngle = Math.atan2(lastMouseY - origin.y, lastMouseX - origin.x); // 上一次鼠标距离中心点的斜率
const currentAngle = Math.atan2(mouseY - origin.y, mouseX - origin.x); // 本次斜率
rotateAngle += Math.round((currentAngle - lastAngle) * 180 / Math.PI); // 转成角度
this.props.rotating(rotateAngle);
this.setState({rotateAngle});
// atan2() 方法可返回从 x 轴到点 (x,y) 之间的角度。
// 返回值是-PI 到 PI 之间的值,是从 X 轴正向逆时针旋转到点 (x,y) 时经过的角
// 弧度=角度*Math.PI/180
2. 同样计算出新的鼠标离中心点的弧度
3. 两者相减得到最新的角度(0-360)之间
拉伸操作
发生在顶级元素mousemove阶段
1. 分两种情况,一种是旋转角度为0,一种是旋转角度不为0
2. 旋转角度为0: 和移动操作类似,如
向左拉长:
top不变
left不变
新的width = 当前(旧的)width + 差值left
向下拉长:
top不变
left不变
新的height = 当前(旧的)height+ 差值top
3.旋转角度不为0:
- 需要结合当前角度计算出差值left和top,如45%时往下移动100px
相当于: 移动(斜边) = 差值top(对边) / (sin45° = 根号2)
top不变
left不变
新的height = 当前(旧的)height+ 差值top / sin45°
成果
http://www.weshowbest.net/scene/create
个人demo:https://wxwxnzm.github.io/
代码实例
https://github.com/wxwxnzm/react-h5-editor/blob/master/src/Drr/index.js
过程中遇到的难点
1,编辑时相对与画布左上角绝对定位,手机预览时如何适配不同屏幕?
2:如何处理scale放大后后节点的按钮被覆盖的问题?
3:如何把网页截图并下载?
1,编辑时相对与画布左上角绝对定位,手机预览时如何适配不同屏幕?
目前解决方案:
1:更改viewport,如根据编辑画布比例(320/640)和实际手机的宽高比例做整个网页的缩放。
例子:https://d.eqxiu.com/s/gRS81n6w
2:viewport不变,内容区根据比例做scale
例子:
http://www.weshowbest.net/preview/165
两者对比好坏探讨
2:如何处理scale放大后后节点的按钮被覆盖的问题?
http://www.weshowbest.net/scene/create
目前解决
用 getBoundingClientRect()方法动态计算放大后的高度,利用绝对定位,赋予按钮top值
探讨有无更好方案
3:如何把网页截图并下载?
https://github.com/niklasvh/html2canvas
利用html2canvas把dom转换成canvas并拿到canvas对象,再用toDataURL等api赋予a标签href并模拟点击(link.click())下载
拓展运用:canvas还可以用来上传图片(文件上传FormData对象)