前段时间 需要在小程序上面做一个功能,类似于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