html图片透视,HTML5 Canvas两点透视图

JavaScript

语言:

JaveScriptBabelCoffeeScript

确定

// Vector point

class Vec {

constructor(x = 0, y = 0) {

this.x = x;

this.y = y;

}

set(x, y) {

this.x = x;

this.y = y;

}

copy(p) {

this.x = p.x;

this.y = p.y;

}

}

// Faces

class Face {

constructor(cube, p0, p1, p2, p3, fill) {

this.p0 = cube.points[p0];

this.p1 = cube.points[p1];

this.p2 = cube.points[p2];

this.p3 = cube.points[p3];

this.fill = fill;

}

draw() {

if (

((this.p1.y - this.p0.y) / (this.p1.x - this.p0.x) -

(this.p2.y - this.p0.y) / (this.p2.x - this.p0.x) < 0) ^

(this.p0.x <= this.p1.x == this.p0.x > this.p2.x)

) {

ctx.beginPath();

ctx.globalCompositeOperation = "source-over";

ctx.strokeStyle = "#000";

ctx.lineJoin = "round";

ctx.fillStyle = this.fill;

ctx.lineWidth = 3;

ctx.moveTo(this.p0.x, this.p0.y);

ctx.lineTo(this.p1.x, this.p1.y);

ctx.lineTo(this.p2.x, this.p2.y);

ctx.lineTo(this.p3.x, this.p3.y);

ctx.closePath();

ctx.fill();

ctx.stroke();

}

}

}

// Cubes

class Cube {

constructor(ox, oy, oz, lf, rf, hf) {

this.ox = ox * 1000;

this.oy = oy * 1000;

this.oz = oz;

this.lf = lf;

this.rf = rf;

this.hf = hf * 2000;

this.points = [];

for (let i = 0; i < 8; i++) {

this.points.push(

new Vec()

);

}

this.s = new Vec();

this.faces = [

new Face(this, 0, 1, 2, 3, '#fff'),

new Face(this, 0, 4, 5, 1, '#888'),

new Face(this, 7, 5, 4, 6, '#fff'),

new Face(this, 2, 7, 6, 3, '#fff'),

new Face(this, 1, 5, 7, 2, '#ddd'),

new Face(this, 0, 3, 6, 4, '#333')

];

}

// draw cube

draw() {

for (let face of this.faces) {

face.draw();

}

}

// draw vanishing lines

drawVP() {

ctx.beginPath();

ctx.globalCompositeOperation = "destination-over";

ctx.strokeStyle = "#f80";

ctx.lineWidth = 1;

[2, 3, 7, 6].forEach((i) => {

ctx.moveTo(vpRight.x, vpRight.y);

ctx.lineTo(this.points[i].x, this.points[i].y);

});

[7, 6, 5, 4].forEach((i) => {

ctx.moveTo(vpLeft.x, vpLeft.y);

ctx.lineTo(this.points[i].x, this.points[i].y);

});

ctx.stroke();

}

// two points projection

project(ox, oy) {

this.s.set(ox + this.ox, oy - this.oy);

project.rl(this.s, this.oz, this.oz);

project.pr(this.points[0]);

project.rl(this.points[0], this.lf, this.rf);

this.points[3].copy(project.r);

this.points[4].copy(project.l);

project.pr(this.points[6]);

this.s.y -= this.hf;

project.rl(this.s, this.oz, this.oz);

project.pr(this.points[1]);

project.rl(this.points[1], this.lf, this.rf);

this.points[2].copy(project.r);

this.points[5].copy(project.l);

project.pr(this.points[7]);

}

}

// two points projection functions

const project = {

r: new Vec(),

l: new Vec(),

rl: function(p, l, r) {

this.r.set(p.x + (vpRight.x - p.x) * r, vpRight.y + (p.y - vpRight.y) * (1 - r));

this.l.set(p.x + (vpLeft.x - p.x) * l, vpLeft.y + (p.y - vpLeft.y) * (1 - l));

},

pr: function(p) {

const a = (this.r.y - vpRight.y) / (this.r.x - vpLeft.x);

const b = (vpLeft.y - this.l.y) / (vpRight.x - this.l.x);

const c = ((vpRight.x - vpLeft.x) * this.l.y - (this.l.x - vpLeft.x) * vpLeft.y) / (vpRight.x - this.l.x);

p.set((c - vpLeft.y) / (a - b) + vpLeft.x, a * (c - vpLeft.y) / (a - b) + vpLeft.y);

}

};

// set canvas

const canvas = {

width: 0,

height: 0,

elem: document.createElement('canvas'),

init: function() {

const ctx = this.elem.getContext('2d');

document.body.appendChild(this.elem);

this.width = this.elem.width = 2000;

this.height = this.elem.height = 1000;

vpLeft.set(this.width * 0.2, this.height * 0.45);

vpRight.set(this.width - this.width * 0.2, this.height * 0.45);

return ctx;

}

};

// set pointer

const pointer = {

x: 0,

y: 0,

cx: 0,

cy: 0,

move: function(e) {

let touchMode = e.targetTouches,

p;

if (touchMode) {

e.preventDefault();

p = touchMode[0];

} else p = e;

this.x = -canvas.elem.offsetLeft + p.clientX * canvas.width / canvas.elem.offsetWidth;

this.y = -canvas.elem.offsetTop + p.clientY * canvas.height / canvas.elem.offsetHeight;

},

lerp: function() {

this.cx += (this.x - this.cx) * 0.1;

this.cy += (this.y - this.cy) * 0.1;

}

};

window.addEventListener('mousemove', e => pointer.move(e), false);

canvas.elem.addEventListener('touchmove', e => pointer.move(e), false);

// main loop

function run() {

requestAnimationFrame(run);

ctx.clearRect(0, 0, canvas.width, canvas.height);

pointer.lerp();

ctx.fillStyle = "rgba(255,255,255,0.1)";

ctx.fillRect(0, 0, canvas.width, vpRight.y);

ctx.moveTo(0, vpLeft.y);

ctx.lineTo(canvas.width, vpRight.y);

ctx.stroke();

vpLeft.draw();

vpRight.draw();

for (let cube of cubes) {

cube.project(pointer.cx, pointer.cy);

cube.drawVP();

for (let face of cube.faces) {

face.draw();

}

}

}

// init

const vpLeft = new Vec();

const vpRight = new Vec();

vpLeft.draw = vpRight.draw = function() {

ctx.beginPath();

ctx.arc(this.x, this.y, 6, 0, 2 * Math.PI);

ctx.fillStyle = "#fff";

ctx.fill();

};

const ctx = canvas.init();

const cubes = new Set();

// scene

cubes.add(new Cube(0, -3, 0.8, 0.08, 0.08, 3));

cubes.add(new Cube(1.6, -1.6, 0.6, 0.4, 0.8, 0.15));

cubes.add(new Cube(0, -0.5, 0.5, 0.5, 0.5, 0.5));

cubes.add(new Cube(0.8, 0.1, 0.4, 0.2, 0.2, 0.2));

cubes.add(new Cube(0.8, -0.5, 0.4, 0.2, 0.2, 0.2));

cubes.add(new Cube(-0.4, -0.5, 0.1, 0.8, 0.1, 0.5));

// start

pointer.x = pointer.cx = canvas.width / 2;

pointer.y = canvas.height / 2;

pointer.cy = 20000;

run();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值