canvas旋轉(zhuǎn)移動的矩陣換算模板

?前言
前段時間? 需要在小程序上面做一個功能,類似于fabricJS庫的canvas編輯效果,但是小程序沒有Windows對象,所以需要魔改fabricJS? ,后來又打算自己模仿一個類似的, 但是遇到一個問題,就是圖片在設(shè)置旋轉(zhuǎn)以后,畫布就變得很奇怪,移動圖片就發(fā)生XY軸偏差的問題,后來參閱資料發(fā)現(xiàn)需要用到一個矩陣換算的公式,進過長時間的專研,最后發(fā)現(xiàn)一個大佬的帖子有個類似的例子,謝謝大佬。
例子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="旋轉(zhuǎn)畫布按鈕" id="rotateBtn">旋轉(zhuǎn)</button>
? ? <p des="渲染當(dāng)前鼠標的X" id="x"></p>
? ? <p des="渲染當(dāng)前鼠標的Y" id="y"></p>
? ? <p des="渲染當(dāng)前旋轉(zhuǎn)的角度" id="rotate"></p>
</body>
<script>
? ? var img = new Image()// 創(chuàng)建一個圖片dom
? ? img.src = './1.png' // 引入圖片路徑
? ? img.width = 290// 設(shè)置圖片寬度
? ? img.height = 440// 設(shè)置圖片高度
? ? canvasWidth = 1200// 設(shè)置畫布寬度
? ? canvasHeight = 900// 設(shè)置畫布高度
? ? let rotate = 0// 設(shè)置一個旋轉(zhuǎn)角度
? ? let x = 100// 設(shè)置X值的初始值
? ? let y = 20// 設(shè)置Y值的初始值
? ? let rotateArr = []// 設(shè)置一個旋轉(zhuǎn)角度數(shù)組
? ? var canvas = document.getElementById('canvas')// 獲取畫布dom節(jié)點
? ? var rotateBtn = document.getElementById('rotateBtn')// 旋轉(zhuǎn)按鈕
? ? var xEl = document.getElementById('x')// 當(dāng)前渲染X值的標簽
? ? var yEl = document.getElementById('y')// 當(dāng)前渲染Y值的標簽
? ? var rotateEl = document.getElementById('rotate') // 當(dāng)前渲染旋轉(zhuǎn)角度的標簽
? ? canvas.width = canvasWidth // 畫布寬度賦值
? ? canvas.height = canvasHeight// 畫布高度賦值
? ? var ctx = canvas.getContext("2d") // 獲取畫布上下文
? ? let move = false // 是否移動
? ? const nextRotateSize = 5// 下一次旋轉(zhuǎn)的角度
? ? // 創(chuàng)建一個矩陣數(shù)組
? ? let m = [1, 0, 0, 1, x, y]
? ? // 插入旋轉(zhuǎn)角度數(shù)值 0-360度
? ? for (let i = 0; i < 360; i++) {
? ? ? ? rotateArr.push(i)
? ? }
? ? // 插入一個0的值 讓數(shù)組的查找的索引從新值指向0
? ? rotateArr.push(0)
? ? // 當(dāng)瀏覽器引入圖片資源的時候? 渲染圖片
? ? 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() // 渲染 當(dāng)前X、Y、rotate的值大小到頁面中
? ? }
? ? // 點擊旋轉(zhuǎn)
? ? rotateBtn.onclick = function () {
? ? ? ? const index = rotateArr.indexOf(rotate) // 查找當(dāng)前選擇角度的索引
? ? ? ? rotate = rotateArr[index + nextRotateSize]// 賦值新的旋轉(zhuǎn)角度
? ? ? ? // 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); // 根據(jù)數(shù)學(xué)方法計算旋轉(zhuǎn)角度的值
? ? ? ? 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()? // 渲染 當(dāng)前X、Y、rotate的值大小到頁面中
? ? }
? ? // 移動圖片? 渲染畫布
? ? function moveImage() {
? ? ? ? if (!move) return // 判斷當(dāng)前是否可以移動
? ? ? ? clearCanvas(ctx, 0, 0, canvasWidth, canvasHeight) // 清空畫布
? ? ? ? drawCanvas(ctx) // 繪制畫布
? ? ? ? setXY()
? ? }
? ? // 當(dāng)鼠標按下時 可以移動
? ? canvas.onmousedown = () => {
? ? ? ? move = true
? ? }
? ? // 當(dāng)鼠標抬起時? 不可以移動
? ? canvas.onmouseup = () => {
? ? ? ? move = false
? ? }
? ? // 當(dāng)鼠標移動時
? ? canvas.onmousemove = (event) => {
? ? ? ? if (!move) return // 判斷當(dāng)前是否可以移動
? ? ? ? m[4] = event.offsetX? // 將鼠標的X軸坐標賦值給矩陣4
? ? ? ? m[5] = event.offsetY //? 將鼠標的Y周坐標賦值給矩陣5
? ? ? ? // console.log(m);?
? ? ? ? moveImage() // 調(diào)用渲染畫布方法
? ? }
? ? /**
? ? ?* @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)? // 清空畫布
? ? }
? ? //? 渲染 當(dāng)前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()// 保存當(dāng)前畫布的狀態(tài)? 入棧
? ? ? ? ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);// 根據(jù)矩陣參數(shù)? 將畫布移至指定的地點
? ? ? ? ctx.fillRect(0, 0, img.width, img.height);// 繪制一個矩形
? ? ? ? ctx.drawImage(img, 0, 0, img.width, img.height)// 繪制圖片
? ? ? ? ctx.restore()// 退出當(dāng)前畫布的狀態(tài)? 返回上一次畫布狀態(tài)? 出棧
? ? }
</script>
</html>
結(jié)語
目前國內(nèi)的小程序canvas庫基本上都是一些簡單的案例,前端的路程依舊遙遠。
無我code