题目比较长,先提供一个图片看看效果

也可以自己查看预览图 在线预览
http://mubanmao.top/content/preview?id=686f9976-3c54-45b1-9e51-1a81389d5fbb
主要的js代码
- let accuracy = 5,
- clothX = 60,
- clothY = 25,
- spacing = 8,
- tearDist = spacing * 4,
- friction = 0.99,
- bounce = 0.5,
- gravity = 400,
- modePull = true;
-
-
- const canvas = document.getElementById("canvas"),
- ctx = canvas.getContext("2d");
-
- ctx.strokeStyle = "#555";
-
- const w = canvas.width = Math.min(700, window.innerWidth),
- h = canvas.height = 400;
-
- const mouse = {
- cut: spacing,
- influence: spacing * 2,
- down: false,
- //button: 1,
- x: 0,
- y: 0,
- px: 0,
- py: 0,
- };
-
- class Point {
- constructor(x, y) {
- this.x = x;
- this.y = y;
- this.px = x;
- this.py = y;
- this.vx = 0;
- this.vy = 0;
- this.pinX = null;
- this.pinY = null;
-
- this.constraints = [];
- }
-
- update(delta) {
- if (this.pinX && this.pinY) { return this; }
-
- if (mouse.down) {
- let dx = this.x - mouse.x;
- let dy = this.y - mouse.y;
- let dist = Math.sqrt(dx * dx + dy * dy);
-
- if (modePull && dist < mouse.influence) {
- this.px = this.x - (mouse.x - mouse.px);
- this.py = this.y - (mouse.y - mouse.py);
- } else if (dist < mouse.cut) {
- this.constraints = [];
- }
- }
-
- this.addForce(0, gravity);
-
- let nx = this.x + (this.x - this.px) * friction + this.vx * delta;
- let ny = this.y + (this.y - this.py) * friction + this.vy * delta;
-
- this.px = this.x;
- this.py = this.y;
-
- this.x = nx;
- this.y = ny;
-
- this.vy = this.vx = 0;
-
- if (this.x >= w) {
- this.px = w + (w - this.px) * bounce;
- this.x = w;
- } else if (this.x <= 0) {
- this.px *= -1 * bounce;
- this.x = 0;
- }
-
- if (this.y >= h) {
- this.py = h + (h - this.py) * bounce;
- this.y = h;
- } else if (this.y <= 0) {
- this.py *= -1 * bounce;
- this.y = 0;
- }
-
- return this;
- }
-
- draw() {
- let i = this.constraints.length;
- while (i--) this.constraints[i].draw();
- }
-
- resolve() {
- if (this.pinX && this.pinY) {
- this.x = this.pinX;
- this.y = this.pinY;
- return;
- }
-
- this.constraints.forEach(constraint => constraint.resolve());
- }
-
- attach(point) {
- this.constraints.push(new Constraint(this, point));
- }
-
- free(constraint) {
- this.constraints.splice(this.constraints.indexOf(constraint), 1);
- }
-
- addForce(x, y) {
- this.vx += x;
- this.vy += y;
- }
-
- pin(pinx, piny) {
- this.pinX = pinx;
- this.pinY = piny;
- }
- }
-
- class Constraint {
- constructor(p1, p2) {
- this.p1 = p1;
- this.p2 = p2;
- this.length = spacing;
- }
-
- resolve() {
- let dx = this.p1.x - this.p2.x;
- let dy = this.p1.y - this.p2.y;
- let dist = Math.sqrt(dx * dx + dy * dy);
-
- if (dist < this.length) return;
-
- let diff = (this.length - dist) / dist;
-
- if (dist > tearDist) this.p1.free(this);
-
- let mul = diff * 0.5 * (1 - this.length / dist);
-
- let px = dx * mul;
- let py = dy * mul;
-
- !this.p1.pinX && (this.p1.x += px);
- !this.p1.pinY && (this.p1.y += py);
- !this.p2.pinX && (this.p2.x -= px);
- !this.p2.pinY && (this.p2.y -= py);
-
- return this;
- }
-
- draw() {
- ctx.moveTo(this.p1.x, this.p1.y);
- ctx.lineTo(this.p2.x, this.p2.y);
- }
- }
-
- class Cloth {
- constructor(free) {
- this.points = [];
-
- let startX = w / 2 - clothX * spacing / 2;
-
- for (let y = 0; y <= clothY; y++) {
- for (let x = 0; x <= clothX; x++) {
- let point = new Point(startX + x * spacing, 20 + y * spacing);
- !free && y === 0 && point.pin(point.x, point.y);
- x !== 0 && point.attach(this.points[this.points.length - 1]);
- y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)]);
-
- this.points.push(point);
- }
- }
- }
-
- update(delta) {
- let i = accuracy;
-
- while (i--) {
- this.points.forEach(point => {
- point.resolve();
- });
- }
-
- ctx.beginPath();
- this.points.forEach(point => {
- point.update(delta * delta).draw();
- });
- ctx.stroke();
- }
- }
-
- dragTracker({
- container: canvas,
- callbackDragStart: (_, pos) => {
- mouse.down = true;
- mouse.px = mouse.x = pos[0];
- mouse.py = mouse.y = pos[1];
- },
- callback: (_, pos) => {
- mouse.px = mouse.x;
- mouse.py = mouse.y;
- mouse.x = pos[0];
- mouse.y = pos[1];
- },
- callbackDragEnd: () => {
- mouse.down = false;
- },
- });
-
-
- let cloth = new Cloth();
-
- //Animation loop
- (function update(time) {
- ctx.clearRect(0, 0, w, h);
- cloth.update(0.016);
-
- requestAnimationFrame(update);
- })();
-
-
- let _g;
- function zeroG() {
- if(gravity) {
- _g = gravity;
- gravity = 0;
- }
- else {
- gravity = _g;
- }
- }
- function setMode(pull) {
- modePull = pull;
- }
- function reset() {
- cloth = new Cloth(!gravity);
- }
-
-
-
-
-
- // window.requestAnimFrame =
- // window.requestAnimationFrame ||
- // window.webkitRequestAnimationFrame ||
- // window.mozRequestAnimationFrame ||
- // window.oRequestAnimationFrame ||
- // window.msRequestAnimationFrame ||
- // function (callback) {
- // window.setTimeout(callback, 1e3 / 60)
- // }
-
- // let accuracy = 5
- // let gravity = 400
- // let clothY = 24
- // let clothX = 50
- // let spacing = 8
- // let tearDist = 60
- // let friction = 0.99
- // let bounce = 0.5
-
- // let canvas = document.getElementById('canvas')
- // let ctx = canvas.getContext('2d')
-
- // canvas.width = Math.min(700, window.innerWidth)
- // canvas.height = 400
-
- // ctx.strokeStyle = '#555'
-
- // let mouse = {
- // cut: 8,
- // influence: 36,
- // down: false,
- // button: 1,
- // x: 0,
- // y: 0,
- // px: 0,
- // py: 0
- // }
-
- // class Point {
- // constructor (x, y) {
- // this.x = x
- // this.y = y
- // this.px = x
- // this.py = y
- // this.vx = 0
- // this.vy = 0
- // this.pinX = null
- // this.pinY = null
-
- // this.constraints = []
- // }
-
- // update (delta) {
- // if (this.pinX && this.pinY) return this
-
- // if (mouse.down) {
- // let dx = this.x - mouse.x
- // let dy = this.y - mouse.y
- // let dist = Math.sqrt(dx * dx + dy * dy)
-
- // if (mouse.button === 1 && dist < mouse.influence) {
- // this.px = this.x - (mouse.x - mouse.px)
- // this.py = this.y - (mouse.y - mouse.py)
- // } else if (dist < mouse.cut) {
- // this.constraints = []
- // }
- // }
-
- // this.addForce(0, gravity)
-
- // let nx = this.x + (this.x - this.px) * friction + this.vx * delta
- // let ny = this.y + (this.y - this.py) * friction + this.vy * delta
-
- // this.px = this.x
- // this.py = this.y
-
- // this.x = nx
- // this.y = ny
-
- // this.vy = this.vx = 0
-
- // if (this.x >= canvas.width) {
- // this.px = canvas.width + (canvas.width - this.px) * bounce
- // this.x = canvas.width
- // } else if (this.x <= 0) {
- // this.px *= -1 * bounce
- // this.x = 0
- // }
-
- // if (this.y >= canvas.height) {
- // this.py = canvas.height + (canvas.height - this.py) * bounce
- // this.y = canvas.height
- // } else if (this.y <= 0) {
- // this.py *= -1 * bounce
- // this.y = 0
- // }
-
- // return this
- // }
-
- // draw () {
- // let i = this.constraints.length
- // while (i--) this.constraints[i].draw()
- // }
-
- // resolve () {
- // if (this.pinX && this.pinY) {
- // this.x = this.pinX
- // this.y = this.pinY
- // return
- // }
-
- // this.constraints.forEach((constraint) => constraint.resolve())
- // }
-
- // attach (point) {
- // this.constraints.push(new Constraint(this, point))
- // }
-
- // free (constraint) {
- // this.constraints.splice(this.constraints.indexOf(constraint), 1)
- // }
-
- // addForce (x, y) {
- // this.vx += x
- // this.vy += y
- // }
-
- // pin (pinx, piny) {
- // this.pinX = pinx
- // this.pinY = piny
- // }
- // }
-
- // class Constraint {
- // constructor (p1, p2) {
- // this.p1 = p1
- // this.p2 = p2
- // this.length = spacing
- // }
-
- // resolve () {
- // let dx = this.p1.x - this.p2.x
- // let dy = this.p1.y - this.p2.y
- // let dist = Math.sqrt(dx * dx + dy * dy)
-
- // if (dist < this.length) return
-
- // let diff = (this.length - dist) / dist
-
- // if (dist > tearDist) this.p1.free(this)
-
- // let mul = diff * 0.5 * (1 - this.length / dist)
-
- // let px = dx * mul
- // let py = dy * mul
-
- // !this.p1.pinX && (this.p1.x += px)
- // !this.p1.pinY && (this.p1.y += py)
- // !this.p2.pinX && (this.p2.x -= px)
- // !this.p2.pinY && (this.p2.y -= py)
-
- // return this
- // }
-
- // draw () {
- // ctx.moveTo(this.p1.x, this.p1.y)
- // ctx.lineTo(this.p2.x, this.p2.y)
- // }
- // }
-
- // class Cloth {
- // constructor (free) {
- // this.points = []
-
- // let startX = canvas.width / 2 - clothX * spacing / 2
-
- // for (let y = 0; y <= clothY; y++) {
- // for (let x = 0; x <= clothX; x++) {
- // let point = new Point(startX + x * spacing, 20 + y * spacing)
- // !free && y === 0 && point.pin(point.x, point.y)
- // x !== 0 && point.attach(this.points[this.points.length - 1])
- // y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)])
-
- // this.points.push(point)
- // }
- // }
- // }
-
- // update (delta) {
- // let i = accuracy
-
- // while (i--) {
- // this.points.forEach((point) => {
- // point.resolve()
- // })
- // }
-
- // ctx.beginPath()
- // this.points.forEach((point) => {
- // point.update(delta * delta).draw()
- // })
- // ctx.stroke()
- // }
- // }
-
- // function setMouse (e) {
- // let rect = canvas.getBoundingClientRect()
- // mouse.px = mouse.x
- // mouse.py = mouse.y
- // mouse.x = e.clientX - rect.left
- // mouse.y = e.clientY - rect.top
- // }
-
- // canvas.onmousedown = (e) => {
- // mouse.button = e.which
- // mouse.down = true
- // setMouse(e)
- // }
-
- // canvas.onmousemove = setMouse
-
- // canvas.onmouseup = () => (mouse.down = false)
-
- // canvas.oncontextmenu = (e) => e.preventDefault()
-
- // let cloth = new Cloth()
-
- // function zeroG() {
- // gravity = 0
- // cloth = new Cloth(true)
- // }
-
- // ;(function update (time) {
- // ctx.clearRect(0, 0, canvas.width, canvas.height)
-
- // cloth.update(0.016)
-
- // window.requestAnimFrame(update)
- // })(0)
- <head>
- <link href="style.scss" rel="stylesheet" type="text/css">
- </head>
- <body>
-
- <canvas id="canvas"></canvas>
-
- <div>
- <label><input type="radio" name="dragmode" onchange="setMode(true)" checked /> Pull</label>
- <label><input type="radio" name="dragmode" onchange="setMode(false)" /> Cut</label>
- <label><input type="checkbox" onchange="zeroG()" /> Zero G</label>
- <p>
- <button onclick="reset()">Reset</button>
- <!--button onclick="zeroG()">Zero G!</button-->
- </p>
- </div>
-
- <script type="text/javascript" src="drag-tracker.js"></script>
-
- <script type="text/javascript" src="Cloth.js"></script>
-
- </body>