!function () {
"use strict";
//获取url后面跟的参数
// var a = document.createElement("A");
// a.href = window.location.href;
// var ret = {},
// seg = a.search.replace(/^\?/, '').split("&"),
// len = seg.length,
// i = 0, s;
// for (; i < len; i++) {
// if (!seg[i]) continue;
// s = seg[i].split("=");
// ret[s[0]] = s[1];
// }
// 创建一个新的 canvas 元素作为缓存
const buffer = document.createElement('canvas');
const bufferCtx = buffer.getContext('2d');
// buffer.width = width * repetitions;
// buffer.height = height;
var RAF = (function () {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var hasDot = false,
hasRect = false,
hasPic = true,
count = 3;
var canvas = document.getElementById("cas");
var ctx = canvas.getContext("2d");
var dots = [];
var idots, ndots;
var img = new Image();
img.src = "http://whxaxes.github.io/canvas-test/src/Funny-demo/transform/img/test.jpg";
img.onload = function () {
var img_w = img.width / 2;
var img_h = img.height / 2;
var left = (canvas.width - img_w) / 2;
var top = (canvas.height - img_h) / 2;
img.width = img_w;
img.height = img_h;
dots = [
{ x: left, y: top },
{ x: left + img_w, y: top },
{ x: left + img_w, y: top + img_h },
{ x: left, y: top + img_h }
];
// dots = [
// { x: 300, y: 100 },
// { x: 300, y: 150 },
// { x: left + img_w, y: top + img_h },
// { x: left, y: top + img_h }
// ];
//获得所有初始点坐标
idots = rectsplit(count, dots[0], dots[1], dots[2], dots[3]);
ndots = rectsplit(count, dots[0], dots[1], dots[2], dots[3]);
render();
};
var area = {};
area = {
x: 400,
y: 200
};
// window.onmousemove = function (e) {
// if (!ndots) return;
// e = e || window.event;
// area = {
// x: e.clientX - canvas.offsetLeft + document.body.scrollLeft + document.documentElement.scrollLeft,
// y: e.clientY - canvas.offsetTop + document.body.scrollTop + document.documentElement.scrollTop
// };
// console.log(area)
// };
//动画循环舞台
var maxDis = 140,
focallength = 250;
animate();
function animate() {
var len = ndots ? ndots.length : 0,
ax = area.x,
ay = area.y,
d, c, scale, dis, xc, yc;
while (len) {
len--;
d = ndots[len];
if (!d.ix) {
d.ix = d.x;
d.iy = d.y;
d.z = 0;
//d.a = 1;
//d.up = false;
}
if (ax == null || ay == null) break;
xc = d.ix - ax;
yc = d.iy - ay;
dis = Math.sqrt(xc * xc + yc * yc);
//将效果放大两倍
d.ez = (maxDis - dis) * 2;
if (d.ez >= 0) {
d.ax = ax;
d.ay = ay;
} else {
d.ez = 0;
}
//赋给目的z轴值ez以及当前z轴值z
c = d.ez - d.z;
d.z += c * 0.1;
// c = d.ez - d.z;
// c = c < 0 ? 0 : c;
// if(d.ez >= d.z){
// if(!d.up) d.a = 0;
// d.up = true;
// d.a += c*0.05;
// d.z += d.a;
// d.z = d.z >= d.ez ? d.ez : d.z
// }else {
// if(d.up) d.a = 0;
// d.up = false;
// d.a -= 1;
// d.z += d.a;
// d.z = d.z <= d.ez ? d.ez : d.z
// }
scale = focallength / (focallength + d.z);
d.x = (d.ax || ax) + (d.ix - (d.ax || ax)) / scale;
d.y = (d.ay || ay) + (d.iy - (d.ay || ay)) / scale;
}
render();
// stats.update();
RAF(animate);
}
/**
* 画布渲染
*/
function render() {
if (!ndots) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillRect(dots[0].x, dots[0].y, img.width, img.height)
ctx.lineWidth = 3;
ctx.strokeStyle = "#fff";
ndots.forEach(function (d, i) {
//获取平行四边形的四个点
var dot1 = ndots[i];
var dot2 = ndots[i + 1];
var dot3 = ndots[i + count + 2];
var dot4 = ndots[i + count + 1];
//获取初始平行四边形的四个点
var idot1 = idots[i];
var idot2 = idots[i + 1];
var idot3 = idots[i + count + 2];
var idot4 = idots[i + count + 1];
if (dot2 && dot3 && i % (count + 1) < count) {
//绘制三角形的下半部分
renderImage(idot3, dot3, idot2, dot2, idot4, dot4);
//绘制三角形的上半部分
renderImage(idot1, dot1, idot2, dot2, idot4, dot4);
}
});
}
/**
* 计算矩阵,同时渲染图片
* @param arg_1
* @param _arg_1
* @param arg_2
* @param _arg_2
* @param arg_3
* @param _arg_3
*/
function renderImage(arg_1, _arg_1, arg_2, _arg_2, arg_3, _arg_3) {
function matrix2() {
/**
* 根据变化前后的点坐标,计算矩阵
* @param arg_1 变化前坐标1
* @param _arg_1 变化后坐标1
* @param arg_2 变化前坐标2
* @param _arg_2 变化后坐标2
* @param arg_3 变化前坐标3
* @param _arg_3 变化后坐标3
* @returns {{a: number, b: number, c: number, d: number, e: number, f: number}}
*/
function getMatrix(arg_1, _arg_1, arg_2, _arg_2, arg_3, _arg_3) {
//传入x值解第一个方程 即 X = ax + cy + e 求ace
//传入的四个参数,对应三元一次方程:ax+by+cz=d的四个参数:a、b、c、d,跟矩阵方程对比c为1
var arr1 = [arg_1.x, arg_1.y, 1, _arg_1.x];
var arr2 = [arg_2.x, arg_2.y, 1, _arg_2.x];
var arr3 = [arg_3.x, arg_3.y, 1, _arg_3.x];
var result = equation(arr1, arr2, arr3);
//传入y值解第二个方程 即 Y = bx + dy + f 求 bdf
arr1[3] = _arg_1.y;
arr2[3] = _arg_2.y;
arr3[3] = _arg_3.y;
var result2 = equation(arr1, arr2, arr3);
//获得a、c、e
var a = result.x;
var c = result.y;
var e = result.z;
//获得b、d、f
var b = result2.x;
var d = result2.y;
var f = result2.z;
return {
a: a,
b: b,
c: c,
d: d,
e: e,
f: f
};
}
/**
* 解三元一次方程,需要传入三组方程参数
* @param arr1 第一组参数
* @param arr2 第二组参数
* @param arr3 第三组参数
* @returns {{x: number, y: number, z: number}}
*/
function equation(arr1, arr2, arr3) {
var a1 = +arr1[0];
var b1 = +arr1[1];
var c1 = +arr1[2];
var d1 = +arr1[3];
var a2 = +arr2[0];
var b2 = +arr2[1];
var c2 = +arr2[2];
var d2 = +arr2[3];
var a3 = +arr3[0];
var b3 = +arr3[1];
var c3 = +arr3[2];
var d3 = +arr3[3];
//分离计算单元
var m1 = c1 - (b1 * c2 / b2);
var m2 = c2 - (b2 * c3 / b3);
var m3 = d2 - (b2 * d3 / b3);
var m4 = a2 - (b2 * a3 / b3);
var m5 = d1 - (b1 * d2 / b2);
var m6 = a1 - (b1 * a2 / b2);
//计算xyz
var x = ((m1 / m2) * m3 - m5) / ((m1 / m2) * m4 - m6);
var z = (m3 - m4 * x) / m2;
var y = (d1 - a1 * x - c1 * z) / b1;
return {
x: x,
y: y,
z: z
}
}
return {
getMatrix: getMatrix,
equation: equation
};
}
ctx.save();
let matrix = matrix2()
//根据变换后的坐标创建剪切区域
ctx.beginPath();
ctx.moveTo(_arg_3.x, _arg_3.y);
ctx.lineTo(_arg_1.x, _arg_1.y);
ctx.lineTo(_arg_2.x, _arg_2.y);
if (hasRect) ctx.stroke();
ctx.closePath();
ctx.clip();
var result;
if (hasPic) {
//传入变换前后的点坐标,计算变换矩阵
if (arg_1.x !== _arg_1.x || arg_1.y !== _arg_1.y || arg_2.x !== _arg_2.x || arg_2.y !== _arg_2.y || arg_3.x !== _arg_3.x || arg_3.y !== _arg_3.y) {
result = matrix.getMatrix(arg_1, _arg_1, arg_2, _arg_2, arg_3, _arg_3);
ctx.transform(result.a, result.b, result.c, result.d, result.e, result.f);
}
//绘制图片
ctx.drawImage(img, idots[0].x, idots[0].y, img.width, img.height);
// 计算起始位置
// const sx = i * width;
// const sy = 0;
// // 绘制图片到缓存中
// bufferCtx.drawImage(img, sx, sy, width, height, i * width, 0, width, height);
}
ctx.restore();
}
/**
* 将abcd四边形分割成n的n次方份,获取n等分后的所有点坐标
* @param n 多少等分
* @param a a点坐标
* @param b b点坐标
* @param c c点坐标
* @param d d点坐标
* 计算出每个小方格的顶点位置
* a-------b
* | |
* | |
* d-------c
*
* @returns {Array}
*/
function rectsplit(n, a, b, c, d) {
//ad向量方向n等分
var ad_x = (d.x - a.x) / n;
var ad_y = (d.y - a.y) / n;
//bc向量方向n等分
var bc_x = (c.x - b.x) / n;
var bc_y = (c.y - b.y) / n;
var ndots = [];
var x1, y1, x2, y2, ab_x, ab_y;
//左边点递增,右边点递增,获取每一次递增后的新的向量,继续n等分,从而获取所有点坐标
for (var i = 0; i <= n; i++) {
//获得ad向量n等分后的坐标
x1 = a.x + ad_x * i;
y1 = a.y + ad_y * i;
//获得bc向量n等分后的坐标
x2 = b.x + bc_x * i;
y2 = b.y + bc_y * i;
for (var j = 0; j <= n; j++) {
//ab向量为:[x2 - x1 , y2 - y1],所以n等分后的增量为除于n
ab_x = (x2 - x1) / n;
ab_y = (y2 - y1) / n;
ndots.push({
x: x1 + ab_x * j,
y: y1 + ab_y * j
})
}
}
return ndots;
}
}()
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>图像3d变形</title>
<style>
body{-moz-user-select: none;}
#cas{
position: absolute;
top: 80px;
left: 0;
right: 0;
margin: auto;
border: 1px solid;
}
.tips{text-align: center;margin: 15px 0;}
.control{ text-align: center; }
</style>
</head>
<body onselectstart="return false">
<div class="tips">Tips:鼠标移至图片处</div>
<div class="control">
<input type="checkbox" id="dot"/><label for="dot">点</label>
<input type="checkbox" id="rect" checked/><label for="rect">方格</label>
<input type="checkbox" id="pic" checked/><label for="pic">贴图</label>
<select name="count" id="count">
<option value="5" selected>5</option>
<option value="15">15</option>
<option value="30">30</option>
</select>
</div>
<canvas id="cas" width="1000" height="600">
亲,你滴浏览器太out啦,换一个吧
</canvas>
</body>
</html>