世界看点:canvas旋转移动的矩阵换算模板

2023-02-25 22:04:39 来源:哔哩哔哩

前言

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


(相关资料图)

例子URL  canvas 中的变换矩阵

> https://juejin.cn/post/6844904063121752077

直接看代码

<!DOCTYPE html>

<html>

<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库基本上都是一些简单的案例,前端的路程依旧遥远。

无我code

标签: 竖直方向 水平方向

推荐阅读>