首页 > 代码库 > HTML5 canvas流体力学效果
HTML5 canvas流体力学效果
某人用Java搞了一个流体力学的演示:http://grantkot.com/MPM/Liquid.html。
下面是 HTML 5版的流体力学演示(推荐使用Chrome浏览器浏览):
效果演示
不过,这仅仅是个开始。某同学将其发布上了reddit.com,于是,全世界的同学们开始给力了。郸城县殳海环保
Flash的开发者首先不服,搞了个 flash版(带源码):http://wonderfl.net/c/yxe9
看到了Flash版,Javascript+HTML5的同学们也不干了,于是出现HTML5版(带源码):http://www.music.mcgill.ca/~sinclair/content/blog/liquid_simulator_ported_to_canvas
不过性能慢了很多,所以,又有人优化了一下HTML5版的程序:http://jsbin.com/unovo4
SVG的同学们也不甘寂寞,不过,那真叫一个慢啊:http://ulo.pe/js-liquid-svg/
这个时候,C/C++同学出来了,使用SDL库也搞了一个:http://q3k.org/fluidsim.zip
短短几天里,被人重写成各种语言。
下面看看在HTML 5里面的实现:
view source print?
001 | < canvas width = "400" height = "400" id = "liquid" ></ canvas >< script > |
002 | /** |
003 | * This version: |
004 | * Copyright Stephen Sinclair (radarsat1) ( http://www.music.mcgill.ca/~sinclair ) |
005 | * MIT License ( http://www.opensource.org/licenses/mit-license.php ) |
006 | * Downloaded from: http://www.music.mcgill.ca/~sinclair/blog |
007 | */ |
008 |
009 | /** |
010 | * Flash version: |
011 | * Copyright iunpin ( http://wonderfl.net/user/iunpin ) |
012 | * MIT License ( http://www.opensource.org/licenses/mit-license.php ) |
013 | * Downloaded from: http://wonderfl.net/c/6eu4 |
014 | */ |
015 |
016 | /** |
017 | * Original Java version: |
018 | * http://grantkot.com/MPM/Liquid.html |
019 | */ |
020 |
021 | var canvas; |
022 | var context; |
023 | var running = false; |
024 | var width = 0; |
025 | var height = 0; |
026 | var liquidTest; |
027 | var step = 0; |
028 |
029 | function LiquidTest(gsizeX, gsizeY, particlesX, particlesY) |
030 | { |
031 | this.particles = []; |
032 |
033 | this.gsizeX = gsizeX; |
034 | this.gsizeY = gsizeY; |
035 |
036 | this.grid = [[]]; //Nodes |
037 | this.active = []; //Nodes |
038 | this.water = new Material(1.0, 1.0, 1.0, 1.0, 1.0, 1.0); |
039 | this.pressed = false; |
040 | this.pressedprev = false; |
041 |
042 | this.mx = 0; |
043 | this.my = 0; |
044 | this.mxprev = 0; |
045 | this.myprev = 0; |
046 |
047 | this.init = function() |
048 | { |
049 | var i = 0, j = 0; |
050 | this.grid = []; |
051 | for (i = 0; i < this.gsizeX ; i++) |
052 | { |
053 | this.grid.push([]); |
054 | for ( j = 0 ; j < this.gsizeY; j++) |
055 | { |
056 | this.grid[i].push(new Node()); |
057 | } |
058 | } |
059 |
060 | var p; |
061 | for ( i = 0 ; i < particlesX; i++) |
062 | for ( j = 0 ; j < particlesY; j++) |
063 | { |
064 | p = new Particle(this.water, i + 4, j + 4, 0.0, 0.0); |
065 | this.particles.push(p); |
066 | } |
067 | } |
068 |
069 | this.paint = function () |
070 | { |
071 | context.clearRect(0, 0, width, height); |
072 |
073 | context.beginPath(); |
074 | for (var pi in this.particles) |
075 | { |
076 | var p = this .particles[pi]; |
077 | line(4.0 * p.x, 4.0 * p.y, |
078 | 4.0 * (p.x - p.u), 4.0 * (p.y - p.v)); |
079 | } |
080 |
081 | context.stroke(); |
082 | } |
083 |
084 | this.simulate = function () |
085 | { |
086 | var drag = false ; |
087 | var mdx = 0 .0, mdy = 0 .0; |
088 |
089 | if (this.pressed && this.pressedprev) |
090 | { |
091 | drag = true ; |
092 | mdx = 0 .25 * (this.mx - this.mxprev); |
093 | mdy = 0 .25 * (this.my - this.myprev); |
094 | } |
095 |
096 | this.pressedprev = this.pressed; |
097 | this.mxprev = this.mx; |
098 | this.myprev = this.my; |
099 |
100 | for (var n in this.active) |
101 | this.active[n].clear(); |
102 | this.active.length = 0 ; |
103 |
104 | var i, j; |
105 | var x, y, phi; |
106 | var fx = 0 .0, fy = 0 .0; |
107 | for (var pi in this.particles) |
108 | { |
109 | var p = this .particles[pi]; |
110 | p.cx = parseInt (p.x - 0.5); |
111 | p.cy = parseInt (p.y - 0.5); |
112 |
113 | x = p .cx - p.x; |
114 | p.px[0] = (0.5 * x * x + 1.5 * x + 1.125); |
115 | p.gx[0] = (x + 1.5); |
116 | x += 1.0; |
117 | p.px[1] = (-x * x + 0.75); |
118 | p.gx[1] = (-2.0 * x); |
119 | x += 1.0; |
120 | p.px[2] = (0.5 * x * x - 1.5 * x + 1.125); |
121 | p.gx[2] = (x - 1.5); |
122 |
123 | y = p .cy - p.y; |
124 | p.py[0] = (0.5 * y * y + 1.5 * y + 1.125); |
125 | p.gy[0] = (y + 1.5); |
126 | y += 1.0; |
127 | p.py[1] = (-y * y + 0.75); |
128 | p.gy[1] = (-2.0 * y); |
129 | y += 1.0; |
130 | p.py[2] = (0.5 * y * y - 1.5 * y + 1.125); |
131 | p.gy[2] = (y - 1.5); |
132 |
133 | for (var i = 0 ; i < 3; i++) |
134 | { |
135 | for (var j = 0 ; j < 3; j++) |
136 | { |
137 | var n = this .grid[p.cx + i][p.cy + j]; |
138 | if (!n.active) |
139 | { |
140 | this.active.push(n); |
141 | n.active = true ; |
142 | } |
143 | phi = p.px[i] * p.py[j]; |
144 | n.m += phi * p.mat.m; |
145 | n.d += phi; |
146 | n.gx += p.gx[i] * p.py[j]; |
147 | n.gy += p.px[i] * p.gy[j]; |
148 | } |
149 | } |
150 | } |
151 |
152 | var density, pressure, weight; |
153 | var n01, n02; |
154 | var n11, n12; |
155 | var cx, cy; |
156 | var cxi, cyi; |
157 |
158 | var pdx, pdy; |
159 | var C20, C02, C30, C03; |
160 | var csum1, csum2; |
161 | var C21, C31, C12, C13, C11; |
162 |
163 | var u, u2, u3; |
164 | var v, v2, v3; |
165 |
166 | for (var pi in this.particles) |
167 | { |
168 | var p = this .particles[pi]; |
169 |
170 | cx = parseInt (p.x); |
171 | cy = parseInt (p.y); |
172 | cxi = cx + 1; |
173 | cyi = cy + 1; |
174 |
175 | n01 = this .grid[cx][cy]; |
176 | n02 = this .grid[cx][cyi]; |
177 | n11 = this .grid[cxi][cy]; |
178 | n12 = this .grid[cxi][cyi]; |
179 |
180 | pdx = n11 .d - n01.d; |
181 | pdy = n02 .d - n01.d; |
182 | C20 = 3 .0 * pdx - n11.gx - 2.0 * n01.gx; |
183 | C02 = 3 .0 * pdy - n02.gy - 2.0 * n01.gy; |
184 | C30 = -2.0 * pdx + n11.gx + n01.gx; |
185 | C03 = -2.0 * pdy + n02.gy + n01.gy; |
186 | csum1 = n01 .d + n01.gy + C02 + C03; |
187 | csum2 = n01 .d + n01.gx + C20 + C30; |
188 | C21 = 3 .0 * n12.d - 2.0 * n02.gx - n12.gx - 3.0 * csum1 - C20; |
189 | C31 = -2.0 * n12.d + n02.gx + n12.gx + 2.0 * csum1 - C30; |
190 | C12 = 3 .0 * n12.d - 2.0 * n11.gy - n12.gy - 3.0 * csum2 - C02; |
191 | C13 = -2.0 * n12.d + n11.gy + n12.gy + 2.0 * csum2 - C03; |
192 | C11 = n02 .gx - C13 - C12 - n01.gx; |
193 |
194 | u = p .x - cx; |
195 | u2 = u * u; |
196 | u3 = u * u2; |
197 | v = p .y - cy; |
198 | v2 = v * v; |
199 | v3 = v * v2; |
200 | density = n01 .d + n01.gx * u + n01.gy * v + C20 * u2 + C02 * v2 + C30 * u3 + C03 * v3 + C21 * u2 * v + C31 * u3 * v + C12 * u * v2 + C13 * u * v3 + C11 * u * v; |
201 |
202 | pressure = density - 1.0; |
203 | if (pressure > 2.0) |
204 | pressure = 2.0; |
205 |
206 | fx = 0.0; |
207 | fy = 0.0; |
208 |
209 | if (p.x < 4.0 ) |
210 | fx += p.mat.m * (4.0 - p.x); |
211 | else if (p.x > this.gsizeX - 5) |
212 | fx += p.mat.m * (this.gsizeX - 5 - p.x); |
213 |
214 | if (p.y < 4.0 ) |
215 | fy += p.mat.m * (4.0 - p.y); |
216 | else if (p.y > this.gsizeY - 5) |
217 | fy += p.mat.m * (this.gsizeY - 5 - p.y); |
218 |
219 | if (drag) |
220 | { |
221 | var vx = Math.abs(p.x - 0.25 * this.mx); |
222 | var vy = Math.abs(p.y - 0.25 * this.my); |
223 | if ((vx < 10.0 ) && (vy < 10.0)) |
224 | { |
225 | weight = p .mat.m * (1.0 - vx * 0.10) * (1.0 - vy * 0.10); |
226 | fx += weight * (mdx - p.u); |
227 | fy += weight * (mdy - p.v); |
228 | } |
229 | } |
230 |
231 | for ( i = 0 ; i < 3; i++) |
232 | { |
233 | for ( j = 0 ; j < 3; j++) |
234 | { |
235 | n = this .grid[(p.cx + i)][(p.cy + j)]; |
236 | phi = p.px[i] * p.py[j]; |
237 | n.ax += -((p.gx[i] * p.py[j]) * pressure) + fx * phi; |
238 | n.ay += -((p.px[i] * p.gy[j]) * pressure) + fy * phi; |
239 | } |
240 | } |
241 | } |
242 |
243 | for (var ni in this.active) |
244 | { |
245 | var n = this .active[ni]; |
246 | if (n.m > 0.0) |
247 | { |
248 | n.ax /= n.m; |
249 | n.ay /= n.m; |
250 | n.ay += 0.03; |
251 | } |
252 | } |
253 |
254 | var mu, mv; |
255 | for (var pi in this.particles) |
256 | { |
257 | var p = this.particles[pi]; |
258 | for (i = 0; i < 3 ; i++) |
259 | { |
260 | for ( j = 0 ; j < 3; j++) |
261 | { |
262 | n = this .grid[(p.cx + i)][(p.cy + j)]; |
263 | phi = p.px[i] * p.py[j]; |
264 | p.u += phi * n.ax; |
265 | p.v += phi * n.ay; |
266 | } |
267 | } |
268 | mu = p .mat.m * p.u; |
269 | mv = p .mat.m * p.v; |
270 | for ( i = 0 ; i < 3; i++) |
271 | { |
272 | for ( j = 0 ; j < 3; j++) |
273 | { |
274 | n = this .grid[(p.cx + i)][(p.cy + j)]; |
275 | phi = p.px[i] * p.py[j]; |
276 | n.u += phi * mu; |
277 | n.v += phi * mv; |
278 | } |
279 | } |
280 | } |
281 |
282 | for (var ni in this.active) |
283 | { |
284 | var n = this .active[ni]; |
285 | if (n.m > 0.0) |
286 | { |
287 | n.u /= n.m; |
288 | n.v /= n.m; |
289 | } |
290 | } |
291 |
292 | var gu, gv; |
293 | for (var pi in this.particles) |
294 | { |
295 | var p = this.particles[pi]; |
296 | gu = 0.0; |
297 | gv = 0.0; |
298 | for (var i = 0; i < 3 ; i++) |
299 | { |
300 | for (var j = 0 ; j < 3; j++) |
301 | { |
302 | var n = this .grid[(p.cx + i)][(p.cy + j)]; |
303 | phi = p.px[i] * p.py[j]; |
304 | gu += phi * n.u; |
305 | gv += phi * n.v; |
306 | } |
307 | } |
308 | p.x += gu; |
309 | p.y += gv; |
310 | p.u += 1.0 * (gu - p.u); |
311 | p.v += 1.0 * (gv - p.v); |
312 | if (p.x < 1.0) |
313 | { |
314 | p.x = (1.0 + Math.random() * 0.01); |
315 | p.u = 0 .0; |
316 | } |
317 | else if (p.x > this.gsizeX - 2) |
318 | { |
319 | p.x = (this.gsizeX - 2 - Math.random() * 0.01); |
320 | p.u = 0.0; |
321 | } |
322 | if (p.y < 1.0 ) |
323 | { |
324 | p.y = (1.0 + Math.random() * 0.01); |
325 | p.v = 0 .0; |
326 | } |
327 | else if (p.y > this.gsizeY - 2) |
328 | { |
329 | p.y = (this.gsizeY - 2 - Math.random() * 0.01); |
330 | p.v = 0.0; |
331 | } |
332 | } |
333 | } |
334 |
335 | this.init(); |
336 | } |
337 |
338 | function Node() |
339 | { |
340 | this.m = 0; |
341 | this.d = 0; |
342 | this.gx = 0; |
343 | this.gy = 0; |
344 | this.u = 0; |
345 | this.v = 0; |
346 | this.ax = 0; |
347 | this.ay = 0; |
348 | this.active = false; |
349 | |
350 | this.clear = function() |
351 | { |
352 | this.m = this.d = this.gx = this.gy = this.u = this.v = this.ax = this.ay = 0.0; |
353 | this.active = false; |
354 | } |
355 | } |
356 |
357 | function Particle(mat, x, y, u, v) |
358 | { |
359 | this.mat = mat; |
360 | this.x = x; |
361 | this.y = y; |
362 | this.u = u; |
363 | this.v = v; |
364 |
365 | this.dudx = 0; |
366 | this.dudy = 0; |
367 | this.dvdx = 0; |
368 | this.dvdy = 0; |
369 | this.cx = 0; |
370 | this.cy = 0; |
371 |
372 | this.px = [0,0,0]; |
373 | this.py = [0,0,0]; |
374 | this.gx = [0,0,0]; |
375 | this.gy = [0,0,0]; |
376 | } |
377 |
378 | function Material(m, rd, k, v, d, g) |
379 | { |
380 | this.m = m; |
381 | this.rd = rd; |
382 | this.k = k; |
383 | this.v = v; |
384 | this.d = d; |
385 | this.g = g; |
386 | } |
387 |
388 | function line(x1,y1,x2,y2) { |
389 | context.moveTo(x1,y1); |
390 | context.lineTo(x2,y2); |
391 | } |
392 |
393 | function getPosition(obj) { |
394 | var p = obj.offsetParent; |
395 | var left = obj.offsetLeft; |
396 | var top = obj.offsetTop; |
397 | if (p) { |
398 | var pos = getPosition(p); |
399 | left += pos[0]; |
400 | top += pos[1]; |
401 | } |
402 | return [left, top]; |
403 | } |
404 |
405 | function mouseMoved(event) |
406 | { |
407 | var pos = getPosition(canvas); |
408 | liquidTest.mx = event.pageX - pos[0]; |
409 | liquidTest.my = event.pageY - pos[1]; |
410 | } |
411 |
412 | function mousePressed(event) |
413 | { |
414 | liquidTest.pressed = true; |
415 | } |
416 |
417 | function mouseReleased(event) |
418 | { |
419 | liquidTest.pressed = false; |
420 | } |
421 |
422 | function stop() |
423 | { |
424 | running = false; |
425 | } |
426 |
427 | function start() |
428 | { |
429 | running = true; |
430 | draw(); |
431 | } |
432 |
433 | function restart(gsizeX, gsizeY, particlesX, particlesY) |
434 | { |
435 | liquidTest = new LiquidTest(gsizeX, gsizeY, particlesX, particlesY); |
436 | running = true; |
437 | draw(); |
438 | } |
439 |
440 | function draw() |
441 | { |
442 | // clear |
443 |
444 | // advance simulation |
445 | liquidTest.simulate(); |
446 |
447 | step ++; |
448 | } |
449 |
450 | function init() { |
451 | canvas = document.getElementById(‘liquid‘); |
452 | width = canvas.width; |
453 | height = canvas.height; |
454 | context = canvas.getContext(‘2d‘); |
455 | context.strokeStyle = "#0000FF"; |
456 |
457 | canvas.onmousedown = mousePressed; |
458 | canvas.onmouseup = mouseReleased; |
459 | canvas.onmousemove = mouseMoved; |
460 |
461 | liquidTest = new LiquidTest(100, 100, 50, 50); |
462 |
463 | start(); |
464 | } |
465 |
466 | setInterval(draw, 33); |
467 | setInterval("liquidTest.paint()", 33); |
468 |
469 | init(); |
470 | </ script > |
HTML5 canvas流体力学效果
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。