canvas旋转移动的矩阵换算模板

148 阅读1分钟

前言

前段时间 需要在小程序上面做一个功能,类似于fabricJS库的canvas编辑效果,但是小程序没有Windows对象,所以需要魔改fabricJS ,后来又打算自己模仿一个类似的, 但是遇到一个问题,就是图片在设置旋转以后,画布就变得很奇怪,移动图片就发生XY轴偏差的问题,后来参阅资料发现需要用到一个矩阵换算的公式,进过长时间的专研,最后发现一个大佬的帖子有个类似的例子,谢谢大佬。

例子URL canvas 中的变换矩阵

juejin.cn/post/684490…

实现效果

直接看代码

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <title>canvas矩阵换算</title>
    <style>
        #canvas {
            border: 1px solid red;
        }
    </style>
</head>

<body>
    <canvas des="渲染画布" id="canvas"></canvas>
    <button des="旋转画布按钮" id="rotateBtn">旋转</button>
    <p des="渲染当前鼠标的X" id="x"></p>
    <p des="渲染当前鼠标的Y" id="y"></p>
    <p des="渲染当前旋转的角度" id="rotate"></p>
</body>
<script>
    var img = new Image()// 创建一个图片dom
    img.src = './1.png' // 引入图片路径
    img.width = 290// 设置图片宽度
    img.height = 440// 设置图片高度
    canvasWidth = 1200// 设置画布宽度
    canvasHeight = 900// 设置画布高度
    let rotate = 0// 设置一个旋转角度
    let x = 100// 设置X值的初始值
    let y = 20// 设置Y值的初始值
    let rotateArr = []// 设置一个旋转角度数组
    var canvas = document.getElementById('canvas')// 获取画布dom节点
    var rotateBtn = document.getElementById('rotateBtn')// 旋转按钮
    var xEl = document.getElementById('x')// 当前渲染X值的标签
    var yEl = document.getElementById('y')// 当前渲染Y值的标签
    var rotateEl = document.getElementById('rotate') // 当前渲染旋转角度的标签
    canvas.width = canvasWidth // 画布宽度赋值
    canvas.height = canvasHeight// 画布高度赋值
    var ctx = canvas.getContext("2d") // 获取画布上下文
    let move = false // 是否移动
    const nextRotateSize = 5// 下一次旋转的角度

    // 创建一个矩阵数组
    let m = [1, 0, 0, 1, x, y]
    // 插入旋转角度数值 0-360度
    for (let i = 0; i < 360; i++) {
        rotateArr.push(i)
    }
    // 插入一个0的值 让数组的查找的索引从新值指向0
    rotateArr.push(0)
    // 当浏览器引入图片资源的时候  渲染图片
    img.onload = function () {
        imageRender()
    }
    /**
     * @description 图片渲染
     */
    function imageRender() {
        clearCanvas(ctx, 0, 0, canvasWidth, canvasHeight)  // 清空画布
        ctx.fillRect(m[4], m[5], img.width, img.height); // 绘制一个矩形
        ctx.drawImage(img, m[4], m[5], img.width, img.height) // 绘制图片
        setXY() // 渲染 当前X、Y、rotate的值大小到页面中
    }
    // 点击旋转
    rotateBtn.onclick = function () {
        const index = rotateArr.indexOf(rotate) // 查找当前选择角度的索引
        rotate = rotateArr[index + nextRotateSize]// 赋值新的旋转角度
        // a:水平方向的缩放
        // b:水平方向的倾斜偏移
        // c:竖直方向的倾斜偏移
        // d:竖直方向的缩放
        // dx:水平方向的移动
        // dy:竖直方向的移动
        //setTransform(a, b, c, d, x, y)
        /**
         * a c x
         * b d y
         * 0 0 1
         */
        let a = (rotate * Math.PI / 180); // 根据数学方法计算旋转角度的值
        let sin = Math.sin(a);// 计算sinθ值
        let cos = Math.cos(a);// 计算cosθ值
        m[0] = cos // 将计算的cosθ值赋值给矩阵0的索引  
        m[1] = sin // 将计算的sinθ值赋值给矩阵1的索引
        m[2] = -sin// 将计算的-sinθ值赋值给矩阵2的索引
        m[3] = cos // 将计算的cosθ值赋值给矩阵3的索引 
        clearCanvas(ctx, 0, 0, canvasWidth, canvasHeight)  // 清空画布
        drawCanvas(ctx)// 绘制画布
        setXY()  // 渲染 当前X、Y、rotate的值大小到页面中

    }

    // 移动图片  渲染画布
    function moveImage() {
        if (!move) return // 判断当前是否可以移动
        clearCanvas(ctx, 0, 0, canvasWidth, canvasHeight) // 清空画布
        drawCanvas(ctx) // 绘制画布
        setXY()
    }
    // 当鼠标按下时 可以移动
    canvas.onmousedown = () => {
        move = true
    }
    // 当鼠标抬起时  不可以移动
    canvas.onmouseup = () => {
        move = false
    }

    // 当鼠标移动时
    canvas.onmousemove = (event) => {
        if (!move) return // 判断当前是否可以移动
        m[4] = event.offsetX  // 将鼠标的X轴坐标赋值给矩阵4
        m[5] = event.offsetY //  将鼠标的Y周坐标赋值给矩阵5
        // console.log(m); 
        moveImage() // 调用渲染画布方法
    }
    /**
     * @description 清空画布
     * @param {Object} ctx 画布上下文
     * @param {Number} startX 清除画布的左上角X
     * @param {Number} startY 清除画布的左上角Y
     * @param {Number} endX 清除画布的右上角X
     * @param {Number} endY 清除画布的右上角Y
     */
    function clearCanvas(ctx, startX, startY, endX, endY) {
        ctx.clearRect(startX, startY, endX, endY)  // 清空画布
    }


    //  渲染 当前X、Y、rotate的值大小到页面中
    function setXY() {
        xEl.innerHTML = m[4]
        yEl.innerHTML = m[5]
        rotateEl.innerHTML = rotate
    }

    /**
     * @description 绘制画布
     * @param {Object} ctx 画布上下文
     */
    function drawCanvas(ctx) {
        ctx.save()// 保存当前画布的状态  入栈
        ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);// 根据矩阵参数  将画布移至指定的地点
        ctx.fillRect(0, 0, img.width, img.height);// 绘制一个矩形
        ctx.drawImage(img, 0, 0, img.width, img.height)// 绘制图片
        ctx.restore()// 退出当前画布的状态  返回上一次画布状态  出栈
    }


</script>

</html>

效果:

结语

目前国内的小程序canvas库基本上都是一些简单的案例,前端的路程依旧遥远。