面条html5,利用HTML5 Canvas实现一碗面条特效

特效描述:利用HTML5 Canvas实现一碗面条特效。利用HTML5 Canvas实现一碗面条特效

代码结构

1. HTML代码

// Initiate Canvas

let can = document.getElementById('can'),

ctx = can.getContext('2d'),

cRad = 250;

can.width = cRad * 2;

can.height = cRad * 2;

ctx.translate(cRad, cRad);

ctx.lineCap = 'round';

ctx.lineJoin = 'round';

// Mouse listeners

let mouse = {

x: -cRad,

y: -cRad

};

can.onmousemove = (e) => {

mouse.x = e.clientX - cRad;

mouse.y = e.clientY - cRad;

};

// World variables

let noodles = [];

const bowlRad = cRad - 50,

mouseRad = 20;

// Object: Node - single Noodle joint

function Node(initX, initY, rad) {

this.p = {x: initX, y: initY};

this.v = {x: 0, y: 0};

this.f = {x: 0, y: 0};

this.r = rad;

this.friction = 0.15;

}

Node.prototype.applyForce = function(dx, dy) {

this.f.x += dx;

this.f.y += dy;

}

Node.prototype.step = function() {

// Stay in Bowl

let centerDis = Math.sqrt((this.p.x * this.p.x) + (this.p.y * this.p.y));

if (centerDis > bowlRad) {

let ang = Math.atan2(this.p.y, this.p.x);

this.applyForce(

(bowlRad - centerDis) * Math.cos(ang),

(bowlRad - centerDis) * Math.sin(ang)

);

}

// Mouse interaction

let mouseDis = Math.sqrt(Math.pow(mouse.x - this.p.x, 2) + Math.pow(mouse.y - this.p.y, 2));

if (mouseDis < this.r + mouseRad) {

let ang = Math.atan2(this.p.y - mouse.y, this.p.x - mouse.x);

this.applyForce(

Math.sqrt(this.r + mouseRad / mouseDis) * Math.cos(ang),

Math.sqrt(this.r + mouseRad / mouseDis) * Math.sin(ang)

);

}

// Apply Movement

this.applyForce(this.v.x * -this.friction, this.v.y * -this.friction);

this.v.x += this.f.x;

this.v.y += this.f.y;

this.p.x += this.v.x;

this.p.y += this.v.y;

this.f = {x: 0, y: 0};

}

// Object: Noodle - a string of connected nodes

function Noodle(initX, initY, length, thickness) {

this.nodes = [];

this.elastic = 0.4;

this.thickness = thickness;

// Random colour

let lightness = (50 + Math.round(Math.random() * 40));

this.color = 'hsl(48, 93%, ' + lightness + '%)';

this.colorOutline = 'hsl(48, 93%, ' + (lightness - 20) + '%)';

// Initiate nodes, slightly out of line

let nodeNum = length / thickness,

offsetY = 0;

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

this.nodes.push(new Node(

initX + (i * thickness),

initY + offsetY,

thickness / 2

));

offsetY += (Math.random() * thickness * 2) - thickness;

}

noodles.push(this);

}

// Draw noodle as curve connecting nodes

Noodle.prototype.draw = function() {

ctx.beginPath();

ctx.moveTo(this.nodes[0].p.x, this.nodes[0].p.y);

let n = 1;

for (; n < this.nodes.length - 2; n++) {

let xc = (this.nodes[n].p.x + this.nodes[n + 1].p.x) / 2,

yc = (this.nodes[n].p.y + this.nodes[n + 1].p.y) / 2;

ctx.quadraticCurveTo(this.nodes[n].p.x, this.nodes[n].p.y, xc, yc);

}

ctx.quadraticCurveTo(

this.nodes[n].p.x,

this.nodes[n].p.y,

this.nodes[n + 1].p.x,

this.nodes[n + 1].p.y

);

ctx.strokeStyle = this.colorOutline;

ctx.lineWidth = this.thickness + 2;

ctx.stroke();

ctx.strokeStyle = this.color;

ctx.lineWidth = this.thickness;

ctx.stroke();

}

// Apply restoration force to all nodes to keep them in line

Noodle.prototype.step = function() {

for(let i = 0; i < this.nodes.length; i++) {

let n = this.nodes[i];

if (i > 0) {

// Find closest distance between previous node

let nPrev = this.nodes[i - 1],

ang = Math.atan2(nPrev.p.y - n.p.y, nPrev.p.x - n.p.x),

nearN = {

x: n.p.x + (Math.cos(ang) * n.r),

y: n.p.y + (Math.sin(ang) * n.r)

},

nearNp = {

x: nPrev.p.x + (Math.cos(ang + Math.PI) * n.r),

y: nPrev.p.y + (Math.sin(ang + Math.PI) * n.r)

};

n.applyForce(

(nearNp.x - nearN.x) * this.elastic,

(nearNp.y - nearN.y) * this.elastic

);

nPrev.applyForce(

(nearN.x - nearNp.x) * this.elastic,

(nearN.y - nearNp.y) * this.elastic

);

}

n.step();

}

}

// World loop

function step() {

ctx.clearRect(-cRad, -cRad, cRad * 2, cRad * 2);

noodles.forEach(n => n.step());

// Draw Bowl

ctx.beginPath();

ctx.arc(0, 0, bowlRad + 25, 0, Math.PI * 2);

ctx.fillStyle = '#e2d3ad';

ctx.fill();

ctx.strokeStyle = '#333';

ctx.lineWidth = 32;

ctx.stroke();

ctx.strokeStyle = '#ddd';

ctx.lineWidth = 30;

ctx.stroke();

// Draw Noodles

noodles.forEach(n => n.draw());

window.requestAnimationFrame(step);

}

// Initiate Noodles

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

let initX = Math.round(-bowlRad + (Math.random() * bowlRad)),

initY = Math.round(-bowlRad + (Math.random() * bowlRad * 2)),

length = 100 + (Math.random() * 250),

thick = Math.round(10 + (Math.random() * 15));

new Noodle(initX, initY, length, thick);

}

// Initiate World

step();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值