• js实现的模拟弹性网格布拉扯仿真动画可拉伸可切割


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

     也可以自己查看预览图 在线预览icon-default.png?t=M5H6http://mubanmao.top/content/preview?id=686f9976-3c54-45b1-9e51-1a81389d5fbb

    主要的js代码

    1. let accuracy = 5,
    2. clothX = 60,
    3. clothY = 25,
    4. spacing = 8,
    5. tearDist = spacing * 4,
    6. friction = 0.99,
    7. bounce = 0.5,
    8. gravity = 400,
    9. modePull = true;
    10. const canvas = document.getElementById("canvas"),
    11. ctx = canvas.getContext("2d");
    12. ctx.strokeStyle = "#555";
    13. const w = canvas.width = Math.min(700, window.innerWidth),
    14. h = canvas.height = 400;
    15. const mouse = {
    16. cut: spacing,
    17. influence: spacing * 2,
    18. down: false,
    19. //button: 1,
    20. x: 0,
    21. y: 0,
    22. px: 0,
    23. py: 0,
    24. };
    25. class Point {
    26. constructor(x, y) {
    27. this.x = x;
    28. this.y = y;
    29. this.px = x;
    30. this.py = y;
    31. this.vx = 0;
    32. this.vy = 0;
    33. this.pinX = null;
    34. this.pinY = null;
    35. this.constraints = [];
    36. }
    37. update(delta) {
    38. if (this.pinX && this.pinY) { return this; }
    39. if (mouse.down) {
    40. let dx = this.x - mouse.x;
    41. let dy = this.y - mouse.y;
    42. let dist = Math.sqrt(dx * dx + dy * dy);
    43. if (modePull && dist < mouse.influence) {
    44. this.px = this.x - (mouse.x - mouse.px);
    45. this.py = this.y - (mouse.y - mouse.py);
    46. } else if (dist < mouse.cut) {
    47. this.constraints = [];
    48. }
    49. }
    50. this.addForce(0, gravity);
    51. let nx = this.x + (this.x - this.px) * friction + this.vx * delta;
    52. let ny = this.y + (this.y - this.py) * friction + this.vy * delta;
    53. this.px = this.x;
    54. this.py = this.y;
    55. this.x = nx;
    56. this.y = ny;
    57. this.vy = this.vx = 0;
    58. if (this.x >= w) {
    59. this.px = w + (w - this.px) * bounce;
    60. this.x = w;
    61. } else if (this.x <= 0) {
    62. this.px *= -1 * bounce;
    63. this.x = 0;
    64. }
    65. if (this.y >= h) {
    66. this.py = h + (h - this.py) * bounce;
    67. this.y = h;
    68. } else if (this.y <= 0) {
    69. this.py *= -1 * bounce;
    70. this.y = 0;
    71. }
    72. return this;
    73. }
    74. draw() {
    75. let i = this.constraints.length;
    76. while (i--) this.constraints[i].draw();
    77. }
    78. resolve() {
    79. if (this.pinX && this.pinY) {
    80. this.x = this.pinX;
    81. this.y = this.pinY;
    82. return;
    83. }
    84. this.constraints.forEach(constraint => constraint.resolve());
    85. }
    86. attach(point) {
    87. this.constraints.push(new Constraint(this, point));
    88. }
    89. free(constraint) {
    90. this.constraints.splice(this.constraints.indexOf(constraint), 1);
    91. }
    92. addForce(x, y) {
    93. this.vx += x;
    94. this.vy += y;
    95. }
    96. pin(pinx, piny) {
    97. this.pinX = pinx;
    98. this.pinY = piny;
    99. }
    100. }
    101. class Constraint {
    102. constructor(p1, p2) {
    103. this.p1 = p1;
    104. this.p2 = p2;
    105. this.length = spacing;
    106. }
    107. resolve() {
    108. let dx = this.p1.x - this.p2.x;
    109. let dy = this.p1.y - this.p2.y;
    110. let dist = Math.sqrt(dx * dx + dy * dy);
    111. if (dist < this.length) return;
    112. let diff = (this.length - dist) / dist;
    113. if (dist > tearDist) this.p1.free(this);
    114. let mul = diff * 0.5 * (1 - this.length / dist);
    115. let px = dx * mul;
    116. let py = dy * mul;
    117. !this.p1.pinX && (this.p1.x += px);
    118. !this.p1.pinY && (this.p1.y += py);
    119. !this.p2.pinX && (this.p2.x -= px);
    120. !this.p2.pinY && (this.p2.y -= py);
    121. return this;
    122. }
    123. draw() {
    124. ctx.moveTo(this.p1.x, this.p1.y);
    125. ctx.lineTo(this.p2.x, this.p2.y);
    126. }
    127. }
    128. class Cloth {
    129. constructor(free) {
    130. this.points = [];
    131. let startX = w / 2 - clothX * spacing / 2;
    132. for (let y = 0; y <= clothY; y++) {
    133. for (let x = 0; x <= clothX; x++) {
    134. let point = new Point(startX + x * spacing, 20 + y * spacing);
    135. !free && y === 0 && point.pin(point.x, point.y);
    136. x !== 0 && point.attach(this.points[this.points.length - 1]);
    137. y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)]);
    138. this.points.push(point);
    139. }
    140. }
    141. }
    142. update(delta) {
    143. let i = accuracy;
    144. while (i--) {
    145. this.points.forEach(point => {
    146. point.resolve();
    147. });
    148. }
    149. ctx.beginPath();
    150. this.points.forEach(point => {
    151. point.update(delta * delta).draw();
    152. });
    153. ctx.stroke();
    154. }
    155. }
    156. dragTracker({
    157. container: canvas,
    158. callbackDragStart: (_, pos) => {
    159. mouse.down = true;
    160. mouse.px = mouse.x = pos[0];
    161. mouse.py = mouse.y = pos[1];
    162. },
    163. callback: (_, pos) => {
    164. mouse.px = mouse.x;
    165. mouse.py = mouse.y;
    166. mouse.x = pos[0];
    167. mouse.y = pos[1];
    168. },
    169. callbackDragEnd: () => {
    170. mouse.down = false;
    171. },
    172. });
    173. let cloth = new Cloth();
    174. //Animation loop
    175. (function update(time) {
    176. ctx.clearRect(0, 0, w, h);
    177. cloth.update(0.016);
    178. requestAnimationFrame(update);
    179. })();
    180. let _g;
    181. function zeroG() {
    182. if(gravity) {
    183. _g = gravity;
    184. gravity = 0;
    185. }
    186. else {
    187. gravity = _g;
    188. }
    189. }
    190. function setMode(pull) {
    191. modePull = pull;
    192. }
    193. function reset() {
    194. cloth = new Cloth(!gravity);
    195. }
    196. // window.requestAnimFrame =
    197. // window.requestAnimationFrame ||
    198. // window.webkitRequestAnimationFrame ||
    199. // window.mozRequestAnimationFrame ||
    200. // window.oRequestAnimationFrame ||
    201. // window.msRequestAnimationFrame ||
    202. // function (callback) {
    203. // window.setTimeout(callback, 1e3 / 60)
    204. // }
    205. // let accuracy = 5
    206. // let gravity = 400
    207. // let clothY = 24
    208. // let clothX = 50
    209. // let spacing = 8
    210. // let tearDist = 60
    211. // let friction = 0.99
    212. // let bounce = 0.5
    213. // let canvas = document.getElementById('canvas')
    214. // let ctx = canvas.getContext('2d')
    215. // canvas.width = Math.min(700, window.innerWidth)
    216. // canvas.height = 400
    217. // ctx.strokeStyle = '#555'
    218. // let mouse = {
    219. // cut: 8,
    220. // influence: 36,
    221. // down: false,
    222. // button: 1,
    223. // x: 0,
    224. // y: 0,
    225. // px: 0,
    226. // py: 0
    227. // }
    228. // class Point {
    229. // constructor (x, y) {
    230. // this.x = x
    231. // this.y = y
    232. // this.px = x
    233. // this.py = y
    234. // this.vx = 0
    235. // this.vy = 0
    236. // this.pinX = null
    237. // this.pinY = null
    238. // this.constraints = []
    239. // }
    240. // update (delta) {
    241. // if (this.pinX && this.pinY) return this
    242. // if (mouse.down) {
    243. // let dx = this.x - mouse.x
    244. // let dy = this.y - mouse.y
    245. // let dist = Math.sqrt(dx * dx + dy * dy)
    246. // if (mouse.button === 1 && dist < mouse.influence) {
    247. // this.px = this.x - (mouse.x - mouse.px)
    248. // this.py = this.y - (mouse.y - mouse.py)
    249. // } else if (dist < mouse.cut) {
    250. // this.constraints = []
    251. // }
    252. // }
    253. // this.addForce(0, gravity)
    254. // let nx = this.x + (this.x - this.px) * friction + this.vx * delta
    255. // let ny = this.y + (this.y - this.py) * friction + this.vy * delta
    256. // this.px = this.x
    257. // this.py = this.y
    258. // this.x = nx
    259. // this.y = ny
    260. // this.vy = this.vx = 0
    261. // if (this.x >= canvas.width) {
    262. // this.px = canvas.width + (canvas.width - this.px) * bounce
    263. // this.x = canvas.width
    264. // } else if (this.x <= 0) {
    265. // this.px *= -1 * bounce
    266. // this.x = 0
    267. // }
    268. // if (this.y >= canvas.height) {
    269. // this.py = canvas.height + (canvas.height - this.py) * bounce
    270. // this.y = canvas.height
    271. // } else if (this.y <= 0) {
    272. // this.py *= -1 * bounce
    273. // this.y = 0
    274. // }
    275. // return this
    276. // }
    277. // draw () {
    278. // let i = this.constraints.length
    279. // while (i--) this.constraints[i].draw()
    280. // }
    281. // resolve () {
    282. // if (this.pinX && this.pinY) {
    283. // this.x = this.pinX
    284. // this.y = this.pinY
    285. // return
    286. // }
    287. // this.constraints.forEach((constraint) => constraint.resolve())
    288. // }
    289. // attach (point) {
    290. // this.constraints.push(new Constraint(this, point))
    291. // }
    292. // free (constraint) {
    293. // this.constraints.splice(this.constraints.indexOf(constraint), 1)
    294. // }
    295. // addForce (x, y) {
    296. // this.vx += x
    297. // this.vy += y
    298. // }
    299. // pin (pinx, piny) {
    300. // this.pinX = pinx
    301. // this.pinY = piny
    302. // }
    303. // }
    304. // class Constraint {
    305. // constructor (p1, p2) {
    306. // this.p1 = p1
    307. // this.p2 = p2
    308. // this.length = spacing
    309. // }
    310. // resolve () {
    311. // let dx = this.p1.x - this.p2.x
    312. // let dy = this.p1.y - this.p2.y
    313. // let dist = Math.sqrt(dx * dx + dy * dy)
    314. // if (dist < this.length) return
    315. // let diff = (this.length - dist) / dist
    316. // if (dist > tearDist) this.p1.free(this)
    317. // let mul = diff * 0.5 * (1 - this.length / dist)
    318. // let px = dx * mul
    319. // let py = dy * mul
    320. // !this.p1.pinX && (this.p1.x += px)
    321. // !this.p1.pinY && (this.p1.y += py)
    322. // !this.p2.pinX && (this.p2.x -= px)
    323. // !this.p2.pinY && (this.p2.y -= py)
    324. // return this
    325. // }
    326. // draw () {
    327. // ctx.moveTo(this.p1.x, this.p1.y)
    328. // ctx.lineTo(this.p2.x, this.p2.y)
    329. // }
    330. // }
    331. // class Cloth {
    332. // constructor (free) {
    333. // this.points = []
    334. // let startX = canvas.width / 2 - clothX * spacing / 2
    335. // for (let y = 0; y <= clothY; y++) {
    336. // for (let x = 0; x <= clothX; x++) {
    337. // let point = new Point(startX + x * spacing, 20 + y * spacing)
    338. // !free && y === 0 && point.pin(point.x, point.y)
    339. // x !== 0 && point.attach(this.points[this.points.length - 1])
    340. // y !== 0 && point.attach(this.points[x + (y - 1) * (clothX + 1)])
    341. // this.points.push(point)
    342. // }
    343. // }
    344. // }
    345. // update (delta) {
    346. // let i = accuracy
    347. // while (i--) {
    348. // this.points.forEach((point) => {
    349. // point.resolve()
    350. // })
    351. // }
    352. // ctx.beginPath()
    353. // this.points.forEach((point) => {
    354. // point.update(delta * delta).draw()
    355. // })
    356. // ctx.stroke()
    357. // }
    358. // }
    359. // function setMouse (e) {
    360. // let rect = canvas.getBoundingClientRect()
    361. // mouse.px = mouse.x
    362. // mouse.py = mouse.y
    363. // mouse.x = e.clientX - rect.left
    364. // mouse.y = e.clientY - rect.top
    365. // }
    366. // canvas.onmousedown = (e) => {
    367. // mouse.button = e.which
    368. // mouse.down = true
    369. // setMouse(e)
    370. // }
    371. // canvas.onmousemove = setMouse
    372. // canvas.onmouseup = () => (mouse.down = false)
    373. // canvas.oncontextmenu = (e) => e.preventDefault()
    374. // let cloth = new Cloth()
    375. // function zeroG() {
    376. // gravity = 0
    377. // cloth = new Cloth(true)
    378. // }
    379. // ;(function update (time) {
    380. // ctx.clearRect(0, 0, canvas.width, canvas.height)
    381. // cloth.update(0.016)
    382. // window.requestAnimFrame(update)
    383. // })(0)
    1. <head>
    2. <link href="style.scss" rel="stylesheet" type="text/css">
    3. </head>
    4. <body>
    5. <canvas id="canvas"></canvas>
    6. <div>
    7. <label><input type="radio" name="dragmode" onchange="setMode(true)" checked /> Pull</label>
    8. <label><input type="radio" name="dragmode" onchange="setMode(false)" /> Cut</label>
    9. <label><input type="checkbox" onchange="zeroG()" /> Zero G</label>
    10. <p>
    11. <button onclick="reset()">Reset</button>
    12. <!--button onclick="zeroG()">Zero G!</button-->
    13. </p>
    14. </div>
    15. <script type="text/javascript" src="drag-tracker.js"></script>
    16. <script type="text/javascript" src="Cloth.js"></script>
    17. </body>

  • 相关阅读:
    @SpringBootApplication注解SpringBoot深度理解(课时八)
    Spring Cloud【实现用户鉴权(什么是JWT、JWT原理、用户微服务、JWT工具类、用户服务实现JWT鉴权)】(八)
    回溯算法笔记
    对自己LRU算法代码的优化
    已解决ModuleNotFoundError: No module named ‘pyhanlp‘
    2024.7.16日 最新版 docker cuda container tookit下载!
    辅助解决小白遇到的电脑各种问题
    logstash排除特定文件或文件夹不采集上报日志数据
    MyBatis-知识点详解
    Python函数
  • 原文地址:https://blog.csdn.net/kongzhonghu/article/details/125555879