kitewhere / practice

练习
0 stars 0 forks source link

照片墙拼接图片 #9

Open kitewhere opened 6 years ago

kitewhere commented 6 years ago

问题

项目有个照片拼接功能 选取几张照片放上照片墙 照片可以拖动旋转缩放 最后生成一张拼接图片保存到服务器

方案1 canvas

image 直接绘制在 canvas 上 canvas 监听事件 然后根据鼠标坐标判断要操作哪个 image 和执行哪种操作 鼠标点击 image 的四个顶点的4个区域触发缩放和旋转 其他位置触发拖动操作 image 每次变形操作时 重绘 canvas image 在变形时 canvas 会频繁重绘 为了性能一共分了3层 canvas

  1. top 负责监听事件 和绘制当前操作的 image 随鼠标移动事件重绘
  2. middle 绘制其他非操作的 image 只在当前操作 image 改变时才重绘
  3. bottom 绘制照片墙的背景 保持不动

这个方案的难点在于判断当前操作的 image 也就是鼠标点击的位置位于哪个 image 的范围内 对于这种四边形 还是比较好判断的 判断 m 在 tl tr bl br 四个顶点的范围内即可(其实 就是 point in polygon 问题的一个特例 ) 无非在计算4个顶点的时候 要考虑到缩放和旋转 三角函数的知识

最后生成拼接图片时把所有图片都绘制到 top 层 然后生成 base64 发回后端即可

方案2 html2canvas

鼠标事件可以直接绑定到每个 item 上 item 的结构可以这样写

.item
    img
    .tl
    .tr
    .bl
    .br

比方案1优在不用自己计算鼠标位置判断操作元素 item 的旋转缩放移动操作算法和方案1一样 只不过具体实现换成 css 的 transform (要注意transform的先后顺序)

最后调用 html2canvas 把 div 画布 转成 canvas 再得到 base64

其实对于这类简单的 dom 结构 不用 html2canvas 自己遍历 dom 取到 transform 信息 再绘制到 canvas 即可

方案3 svg foreignobject

和方案2类似 只是不用自己遍历 dom 只要把整个 dom 结构塞进 svg 的 里即可 不过最后还是要用canvas 转成 base64

优点就是特别方便 缺点就是 ie 不兼容 foreignobject

function getBase64(img){
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    var width = img.width, 
        height = img.height;
     // canvas绘制
     canvas.width = width;
     canvas.height = height;
    // 画布清除
    context.clearRect(0, 0, width, height);    
    // 绘制图片到canvas
    context.drawImage(img, 0, 0);

    return canvas.toDataURL();
}

function html2Svg (domStr) {  
            //创建模版字符串
            var svgXML=
            `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
                <foreignObject width="100%" height="100%">${generateXML(html)}</foreignObject>
             </svg>`
            //利用Blob创建svg
            var svg = new Blob([svgXML], {type: 'image/svg+xml'})
            //利用DOMURL.createObjectURL取出对象
            var url = window.URL.createObjectURL(svg);
            var img = new Image()
            img.src = url

            return img
        }

// 由于`foreignObject`只能引用XML文档,
// 所以我们需要对DOM进行格式化
function generateXML (domStr) {  
        var doc = document.implementation.createHTMLDocument('');
            doc.write(html);
            doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
            doc = parseStyle(doc)
            console.log(doc)
            html = (new XMLSerializer).serializeToString(doc).replace('<!DOCTYPE html>','');
            return html
}

结论

方案1 的难点在于判断鼠标位置 以及绘制有些繁琐 不过有些 cavans 库 做了封装 比如 createJs 操作起来还算方便 方案3 兼容性一票否决

所以最后还是方案2最好用