Падающий снежок на канвасе :)
Однажды ночью, не так давно, наблюдал очаровательную погоду - стоял не очень сильный мороз, был абсолютный штиль и неспешно присыпал снежок.
И мне захотелось изобразить эту картину в коде.) Не то чтобы круто, да я и не выпендривался, но может кому-нибудь понравится. :) <head> <style> img, canvas { position: absolute; left: 0px; top: 0px; margin: 0px; padding: 0px; border: none; } </style> <script> function byId ( id ) { return document.getElementById( id ); } function rand ( min, max ) { return Math.random() * ( max - min ) + min; } function now () { return ( +new Date() ) / 1000; } function Sprite ( image, sx, sy, sw, sh, originX, originY ) { this.image = image; this.sx = sx; this.sy = sy; this.sw = sw; this.sh = sh; this.originX = originX ? originX : 0; this.originY = originY ? originY : 0; } Sprite.prototype.draw = function ( context, x, y, angle, scaleX, scaleY ) { context.save(); context.translate( x, y ); context.rotate( angle ); context.scale( scaleX, scaleY ); context.drawImage( this.image, this.sx, this.sy, this.sw, this.sh, -this.originX, -this.originY, this.sw, this.sh ); context.restore(); } function Flakes ( canvas, flake, zmin, zmax, count, speed, loopTime ) { var viewW = canvas.width; var viewH = canvas.height; zmax = Math.min( zmax, ( loopTime * speed - flake.height ) / viewH ); zmin = Math.min( zmin, zmax ); var particles = []; var flakeHalfW = flake.width * 0.5; var flakeHalfH = flake.height * 0.5; for ( var i = 0; i < count; i++ ) { var spawnZ = rand( zmin, zmax ); particles.push({ spawnX : rand( -flakeHalfW / spawnZ, viewW + flakeHalfW / spawnZ ), spawnZ : spawnZ, spawnTime : rand( 0, loopTime ), speed : speed / spawnZ, halfH : flakeHalfH / spawnZ }); } particles.sort( function ( a, b ) { return b.spawnZ - a.spawnZ; }); this.particles = particles; this.viewW = viewW; this.viewH = viewH; this.loopTime = loopTime; this.ctx = canvas.getContext( '2d' ); this.sprite = new Sprite( flake, 0, 0, flake.width, flake.height, flake.width * 0.5, flake.height * 0.5 ); } Flakes.prototype.draw = function ( time ) { var ctx = this.ctx; var sprite = this.sprite; var particles = this.particles; var viewH = this.viewH; var loopTime = this.loopTime; for ( var i = 0; i < particles.length; i++ ) { var particle = particles[ i ]; var y = -particle.halfH + ( ( time + particle.spawnTime ) % loopTime ) * particle.speed; if ( y < viewH + particle.halfH ) { sprite.draw( ctx, particle.spawnX, y, 0, 1 / particle.spawnZ, 1 / particle.spawnZ ); } } } window.onload = function () { var imgBg = byId( 'image-bg' ); var imgFlake = byId( 'image-flake' ); var canvas = byId( 'canvas' ); document.body.removeChild( imgFlake ); canvas.width = imgBg.width; canvas.height = imgBg.height; var ctx = canvas.getContext( '2d' ); var flakes = new Flakes( canvas, imgFlake, 2, 25, 1500, 400, 30 ); function render () { ctx.clearRect( 0, 0, canvas.width, canvas.height ); flakes.draw( now() - timeOffset ); } var animate = true; var timeStop; var timeOffset = 0; var intervalId = window.setInterval( render, 40 ); canvas.onclick = function () { if ( animate = !animate ) { timeOffset += now() - timeStop; intervalId = window.setInterval( render, 40 ); } else { timeStop = now(); window.clearInterval( intervalId ); } } } </script> </head> <body> <img id="image-flake" src="http://dl.dropbox.com/u/19390559/share/snowflake.png"> <img id="image-bg" src="http://dl.dropbox.com/u/19390559/share/christmas-tree.png"> <canvas id="canvas"></canvas> </body> Можно кликнуть по картинке чтобы остановить/возобновить анимацию. ps: фон, конечно же, не мой - нашел в гугле.) |
Классно!
Цитата:
|
Цитата:
|
Спасибо порадовал, очень празднично получилось)))
помню когда javascript только только появился все писали телетайп, бегущий строчки, и снегопад. Снежинки правда gif картинками в dom были, тобишь без канваса. Теперь вот шедевр на канвасе, в следующем году ждём снежинок на webGL ))))) |
На этой картинке выглядит хорошо, хотя местами подтормаживает.
А варианты будут, чтобы снежинки полетели вправо, влево, вверх и вбок, вниз и вбок? |
Красиво, но двадцатню процентов ЦП жрет. Хотя, если останавливать анимацию при открытии другой вкладки - норм. А то задолбал этот снежок, который в фоне на 3 вкладках сжирает все ресурсы.
|
Цитата:
|
Цитата:
Цитата:
Цитата:
Цитата:
Оно-то можно сделать и спиральное падение с осевым вращением, и нелинейный ветер с интерполяцией по динамическому векторному полю и зонами турбулентности. Много чего можно сделать. :) Данное было сделано просто из любопытства - и любопытство я удовлетворил. |
Цитата:
Прикольно, когда пишешь то, с чем потом можно играться. Наверное предложение неуместно, раз это в оффтопе?! |
|
godofjavascript,
комп гавно ![]() А фон кстати у dmitrymar, или как там его ник пишется, не стоял? :) |
Цитата:
|
godofjavascript,
27 - это маловато как бы |
Цитата:
ЦП1 52% 3400МГц ЦП2 34% 1600МГц ЦП3 27% 1600МГц ЦП4 25% 1600МГц Если без скрипта то 2% на 1600 держится. За такие вещи кастрировать нужно. Но так как это не продакшн, а just for fun, то это простительно. |
Цитата:
|
классно! даже натурально.:)
гозар и максимус - вредины! |
Цитата:
Дзен-трансгуманист, зайдём к тебе с приставом в гости, посмотрим что у тебя личное , а что лишнее)) |
Интересно было бы сравнить нагрузку на проц двух одинаковых работ сделанных на канвас и на флеш.. Мне кажется флеш нагружает не меньше.. взять тот же рутрекер - напичкан флешом
|
правильно говорить "на канве", а не "на канвасе"
Цитата:
Цитата:
|
Цитата:
Цитата:
Нагрузка в хроме - 40-50%. Вот так-то. :D Опять же, повторюсь, что производительность напрямую зависит от того, ускорен ли 2d-контекст на конкретном браузере под конкретную систему аппаратно, или нет. А если нет, то от того, насколько эффективны программные алгоритмы. И предполагаю, что на WebGL показатели в среднем были бы лучше - но поскольку я его еще не изучал, то и проверить пока ничего не могу, могу только строить догадки. :) |
а я думал это на нем сделано, это просто 2д контекст?
|
Дзен-трансгуманист, 3-и страницы треда в очередной раз говорят о том, что инициатива наказуема :D
Вроде ничего никому не обещаешь, а они все требуют и требуют ) |
Цитата:
Цитата:
me.on( 'request', function ( request, response ) { response.write( 'ПНХ' ); response.close(); }); Таков им будет мой ответ.))) |
Цитата:
ну если говорить о производительности то часть нагрузки должно давать масштабирование на particle.spawnZ sprite.draw( ctx, particle.spawnX, y, 0, 1/ particle.spawnZ, 1/ particle.spawnZ ); (маштабирование горизонталь + вертикаль )*каждую снежинку * каждый кадр = замедление. без масштабирования у меня прирост производительности вдвое, нагрузка упала c 25% до 12-10% Попробуйте замерить производительность (без масштабирования) у себя сами. <head> <style> img, canvas { position: absolute; left: 0px; top: 0px; margin: 0px; padding: 0px; border: none; } </style> <script> function byId ( id ) { return document.getElementById( id ); } function rand ( min, max ) { return Math.random() * ( max - min ) + min; } function now () { return ( +new Date() ) / 1000; } function Sprite ( image, sx, sy, sw, sh, originX, originY ) { this.image = image; this.sx = sx; this.sy = sy; this.sw = sw; this.sh = sh; this.originX = originX ? originX : 0; this.originY = originY ? originY : 0; } Sprite.prototype.draw = function ( context, x, y, angle, scaleX, scaleY ) { //context.save(); //context.translate( x, y ); //context.rotate( angle ); //context.scale( scaleX, scaleY ); /*context.drawImage( this.image, this.sx, this.sy, this.sw, this.sh, -this.originX, -this.originY, this.sw, this.sh );*/ context.drawImage(this.image, x,y); //context.restore(); } function Flakes ( canvas, flake, zmin, zmax, count, speed, loopTime ) { var viewW = canvas.width; var viewH = canvas.height; zmax = Math.min( zmax, ( loopTime * speed - flake.height ) / viewH ); zmin = Math.min( zmin, zmax ); var particles = []; var flakeHalfW = flake.width * 0.5; var flakeHalfH = flake.height * 0.5; for ( var i = 0; i < count; i++ ) { var spawnZ = rand( zmin, zmax ); particles.push({ spawnX : rand( -flakeHalfW / spawnZ, viewW + flakeHalfW / spawnZ ), spawnZ : spawnZ, spawnTime : rand( 0, loopTime ), speed : speed / spawnZ, halfH : flakeHalfH / spawnZ }); } particles.sort( function ( a, b ) { return b.spawnZ - a.spawnZ; }); this.particles = particles; this.viewW = viewW; this.viewH = viewH; this.loopTime = loopTime; this.ctx = canvas.getContext( '2d' ); this.sprite = new Sprite( flake, 0, 0, flake.width, flake.height, flake.width * 0.5, flake.height * 0.5 ); } Flakes.prototype.draw = function ( time ) { var ctx = this.ctx; var sprite = this.sprite; var particles = this.particles; var viewH = this.viewH; var loopTime = this.loopTime; //временный контекст //var pCanvas = document.createElement('canvas'); //var pCtx = pCanvas.getContext('2d'); //pCanvas.width = 480; //pCanvas.height = 384; for ( var i = 0; i < particles.length; i++ ) { var particle = particles[ i ]; var y = -particle.halfH + ( ( time + particle.spawnTime ) % loopTime ) * particle.speed; if ( y < viewH + particle.halfH ) { //sprite.draw( pCtx, particle.spawnX, y, 0, 1, 1 ); sprite.draw( ctx, particle.spawnX, y, 0, 1, 1 ); } } //ctx.drawImage(pCanvas, 0,0); } window.onload = function () { var imgBg = byId( 'image-bg' ); var imgFlake = byId( 'image-flake' ); var canvas = byId( 'canvas' ); document.body.removeChild( imgFlake ); canvas.width = imgBg.width; canvas.height = imgBg.height; var ctx = canvas.getContext( '2d' ); var flakes = new Flakes( canvas, imgFlake, 2, 25, 1500, 400, 30 ); function render () { ctx.clearRect( 0, 0, canvas.width, canvas.height ); flakes.draw( now() - timeOffset ); } var animate = true; var timeStop; var timeOffset = 0; var intervalId = window.setInterval( render, 40 ); canvas.onclick = function () { if ( animate = !animate ) { timeOffset += now() - timeStop; intervalId = window.setInterval( render, 40 ); } else { timeStop = now(); window.clearInterval( intervalId ); } } } </script> </head> <body> <img id="image-flake" src="http://dl.dropbox.com/u/19390559/share/snowflake.png"> <img id="image-bg" src="http://dl.dropbox.com/u/19390559/share/christmas-tree.png"> <canvas id="canvas"></canvas> </body> В качестве решения можно спрайты готовить заранее а не ресайзить в процессе рендеринга. это конечно всё теории а пробывать лень. Мне кажется что большие снежинки отьедают выигрыш обратно, так как маленькие снежинки прорисовать быстрее, предварительно подготовленные спрайты могут дать представление истинном приросте) UPD - из решения выпилено ещё часть манипуляций с канвасом |
Сейчас перечитал название темы и вспомнил "Брат 2". Там был один сутенер, "ганста-нига" (3:15 сек) на его сленге русские были "снежок". В свете этого, падающий снежок воспринимается по другому. :)
|
DjDiablo,
particle.spawnX и y тоже с плавающей запятой, попробуй Math.round их - будет еще быстрее. А подресайзить заранее конечно можно, но это на каждый спрайт отдельный слой придется готовить, потому что масштаб тут у всех разный. То есть, в данном случае это будет 1500 картинок размером вплоть до 20x20. Еще я, наверно, неправильно поступил, что подготовил все частицы заранее и зациклил их по модулю от времени. Логичнее было бы генерировать их на лету, но это мой первый опыт создания систем частиц, так что бить ногами не надо.) Цитата:
|
Да мне тоже неудаётся воспроизвести результат хз, почему в первый разы так много выдало.
прирост есть но слишком мальнький (в хроме), интересно как в других браузерах. в качестве альтернативы пробую предварителный рендеринг, но здесь он прироста отчего то тоже недаёт толи контекст слишком большой, толи частиц слишком мало чтобы профит ощутить. upd. Выпилил ещё лишние манипуляции с контекстом, кому нелень потестите пример сверху, я его обновил. сейчас у меня 9-10% стабильно VS 13-15% у оригинала |
Дзен-трансгуманист,
Крутая штука. Тоже была идея такое сделать, но до НГ не успел. Потом так и забыл. Спасибо, порадовал. Я даже гозару плюсик поставил чтобы тебя плюсануть. :D Кст, о ограничениях. Я немо опять наверно могу минус поставить. Ща проверим :) |
Цитата:
Опера 12.12 XP двух ядерка 3Гига частота проца |
DjDiablo,
У [html height=600 ] задай. |
http://processingjs.org/ - нашёл недавно.
Потихоньку колупаюсь в своём недавнем приобретении (Arduino). Выяснил что язык для IDE от этой игрушки основан на языке processing, который заточен под работу с графикой. processingjs это порт этого языка на JS. Либа использует canvas. |
Проверил в Лисе - тож самое что и в опере
Ошущение:... что движения в канвас походят на топот солдат по мосту, т.е при определенном соотношении тактовой частоты и периода обновлений картинок 0 все это приходит в резонанс, очевидно при многопроцессорах еще хитрее ============= Зы Проверил на ноуте - эффект обратный - Первопост жрёт порядка 40% и 25% пост от DjDiablo... |
Да тут как раз всё логично.
Опера похоже медленно копирует изображения в канвас. у меня снежинки больше, поэтому и тормозит опера сильнее заремарь context.scale( scaleX, scaleY ); в оригинале, получишь точно такиеже тормоза похоже что опере проще отмасштабировать картинку, чем отрисовать её. у меня в опере мой 25, оригинал 12% после отключения scale в оригинале, оригинал также начинает жрать 25% opera. Версия: 12.00 Сборка:1467 Платформа: Win32 Система: Windows 7 хром v23.0.1271.97 разница 9-10 vs 12-15-17 я уже писал выше. (однако это на ноуте, на PC гляну завтра, хотя непонимаю почему должно отличаться) лису нетестил, вероятно таже болезнь что и у оперы PC I3-2330m 2.2 гц, ноут |
DjDiablo,
В лисе тож самое - я писал ========================== В Хроме - твой пост и Первопост - почти идентично 23% |
Цитата:
|
Часовой пояс GMT +3, время: 21:05. |