В прошлом году подобный конкурс неожиданно привлек большой интерес участников RUso. Было весело, люди хотели продолжение праздника вновь и вновь. К слову, было проведено 4 конкурса с вознаграждением. Почему бы нам не устроить точно такой же, а может быть ещё лучше праздник в наступающем Новом 2020 году!
Я выбрал достаточно нейтральную картинку с тёмным фоном, так как слышал мнения, что неплохо было бы в этом году сделать работы с гирляндами, фейерверками. Рекомендуемые объекты в решении конкурсного задания: Анимация заголовка С Новым 2020 годом! Анимация Деда Мороза, Снегурочки и других новогодних персонажей Новогодняя ёлочка: ёлочные игрушки, гирлянды Анимация звездного неба Анимация снежинок Фейерверки, хлопушки Для примера анимация луны:
Анимация звезды на ёлочке
От одного участника может быть от одного до трех ответов, в отдельных постах. Поздравляем победителя Stranger in the Q! Добавлено объявление в топик-приглашение к конкурсу
Ответ Салют в лесу
let s Math.random = () => (2**31-1&(s=Math.imul(48271,s)))/2**31 let rnd = n => (Math.random()-0.5)*(n||1) let many = (n,f) => Array(n).fill(0).map((e,i) => f(i)) let fireworks = fireworksCanvas.getContext("2d") let forest = forestCanvas.getContext("2d") let g, i, c function newSeg(s, dir, da, w){ return { da, // шаг изменения угла col: s.col, // цвет width: w||s.width - 0.02, // толщина pts: [s.pts[2], s.pts[3]], // точки сегмента dir: dir+da, // направление (угол) текущего сегмента len: s.len, // длина сегмента sw: s.sw // коэффициент для поворота (наследуется от прошлого сегмента) } } // алгоритм "роста" сегментов на основе данных о прошлом сегменте function grow() { i = 0, c = rnd(360) let da = rnd() // случайный компонент поворота let n = 5+Math.floor(Math.random()*19) // кол-во ветвей let s = 2 + rnd(1) // размер сегмента let pts = [(0.25+Math.random()*0.5)*innerWidth, (0.2+Math.random())*innerHeight/3] g = many(n, i => ({ pts, dir: Math.PI/n*i*2 + rnd() + da, len: s, width: 10, sw: 1.01 + rnd(0.02), col: `hsl(${Math.random()*360},66%,66%)` })) requestAnimationFrame(growIteration) } function growStep(count) { fireworks.fillStyle="#00000014" fireworks.fillRect(-1e5,-1e5,2e5,2e5) for (var j=0 j { calcSegment(s) paintSegment(s) return growAlgorithm(s) }) } } // алгоритм "роста" function growAlgorithm(s) { let result = [] if(s.width < 0) return result let sw = rnd(0.05) // небольшой коэф. для поворота // если толщина сегмента больше 1 то с какой-то вероятностью делим ветвь на 3 if (s.width>1 && rnd() > 0.45) { let dir = 0.5 + rnd(0.5) // случайное направление let w = s.width/2 +0.5 // делаем новые ветви тоньше result.push(newSeg(s, s.dir+dir + rnd(), sw, w)) result.push(newSeg(s, s.dir-dir+ rnd(), -sw, w)) result.push(newSeg(s, s.dir,-sw, w)) } else if (rnd() > 0.45) { // растем дальше и поворачиваем на коэф. этой итерации result.push(newSeg(s, s.dir, sw)) } else { // или растем дальше и поворачиваем на коэф. текущей ветки result.push(newSeg(s, s.dir, (s.da||0)*s.sw)) } return result } function calcSegment(s) { let x = s.pts[0] + Math.cos(s.dir)*s.len let y = s.pts[1] + Math.sin(s.dir)*s.len s.pts.push(x,y) } function growIteration() { if (g.length) requestAnimationFrame(growIteration) else grow(0,0) growStep(1) } function paintSegment(s) { fireworks.lineWidth = 1 fireworks.lineCap ="round" fireworks.strokeStyle=s.col fireworks.beginPath() fireworks.moveTo(s.pts[0], s.pts[1]) fireworks.lineTo(s.pts[2], s.pts[3]) fireworks.stroke() } function star(c) { c.beginPath() c.arc(Math.random()*innerWidth, Math.random()*innerHeight, Math.random(), 0, 2 * Math.PI ) c.fill() } function moon(c) { c.beginPath() c.arc(100, 100, 30, Math.PI/2, -Math.PI/2) c.bezierCurveTo(75, 85, 75, 115, 100, 130) c.fill() } function snowHill(c){ c.beginPath() c.arc(Math.random()*innerWidth, innerHeight*5+(Math.random()*0.3+0.7)*innerHeight, innerHeight*5, 0, Math.PI*2) c.fill() } function resize() { s = 1 resizeCanvas(fireworksCanvas) resizeCanvas(forestCanvas) forest.fillStyle = 'white' moon(forest) forest.shadowColor = "black" many(100, i => star(forest)) forest.shadowBlur = 7 many(6, i => snowHill(forest)) forest.shadowBlur = 13 many(parseInt(innerWidth/20), i => [ Math.random()*innerWidth, innerHeight - Math.random()*Math.random()*innerHeight/6-Math.min(90,innerHeight/5) ]).sort((a, b) => a[1] - b[1]).map(p => { let ky = p[1]/innerHeight let h = 88+Math.random()*33 let s = 44+Math.random()*10 many(5, i => { forest.fillStyle = `hsl(${h},${s}%,${15 + i*(5 + Math.random()*5)}%)` level(p[0], p[1] - 10*i, ky*70-(i*10), i) }) }) } let treeLevel = [ [ -0.25, 1, -0.5, 2, -1, 2 ], [ -0.5, 2, -0.35, 1.75, -0.2, 1.5 ], [ -0.15, 1.7, -0.15, 1.7, 0, 2.25 ], [ 0.15, 1.7, 0.15, 1.7, 0.15, 1.5 ], [ 0.35, 1.75, 0.5, 2, 1, 2 ],[ 0.5, 2, 0.25, 1, 0, 0 ] ] function level(x,y,s) { s = Math.max(0,s) let c = forest c.beginPath() c.moveTo(x, y) let d = 2 treeLevel.forEach(curve => c.bezierCurveTo( x+s*curve[0]+rnd(d), y+s*curve[1]+rnd(d), x+s*curve[2]+rnd(d), y+s*curve[3]+rnd(d), x+s*curve[4]+rnd(d), y+s*curve[5]+rnd(d) )) c.closePath() c.fill() } function resizeCanvas(canvas) { if (canvas.width !== innerWidth || canvas.height !== innerHeight) { canvas.width = innerWidth canvas.height = innerHeight } } addEventListener("resize", resize) resize() grow() body { background-color: black margin: 0 overflow: hidden } canvas { position: fixed }