В прошлом году подобный конкурс неожиданно привлек большой интерес участников RUso.
Было весело, люди хотели продолжение праздника вновь и вновь.
К слову, было проведено 4 конкурса с вознаграждением.
Я выбрал достаточно нейтральную картинку с тёмным фоном, так как слышал мнения, что неплохо было бы в этом году сделать работы с гирляндами, фейерверками.
Рекомендуемые объекты в решении конкурсного задания:
Для примера анимация луны:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="0 0 73 73" >
<defs>
<radialGradient id="RadialGrad"
fx="50%" fy="50%" r="65%"
spreadMethod="pad">
<stop offset="0%" stop-color="#E7D68C" stop-opacity="1"/>
<stop offset="100%" stop-color="#FFFEED" stop-opacity="1" />
</radialGradient>
</defs>
<rect width="100%" height="100%" />
<g transform="rotate(-20 35.5 35.5)">
<circle cx="35.5" cy="35.5" r="35" stroke="none" fill="url(#RadialGrad)" />
<circle cx="35.5" cy="35.5" r="35" stroke="none" fill="black" >
<animate id="youngMoon" attributeName="cx" values="35.5;-35.5;" begin="1s;oldMoon.end+1s" dur="10s" fill="freeze" />
<animate id="oldMoon" attributeName="cx" values="105;35.5;" begin="youngMoon.end+1s" dur="10s" fill="freeze" />
</circle>
</g>
</svg>
Анимация звезды на ёлочке
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet" >
<defs>
<filter id="glow" filterUnits="userSpaceOnUse"
x="-50%" y="-50%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" stdDeviation="25" result="blur5"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="30" result="blur10"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="40" result="blur20"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="50" result="blur30"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="70" result="blur50"/>
<feMerge result="blur-merged">
<feMergeNode in="blur10"/>
<feMergeNode in="blur20"/>
<feMergeNode in="blur30"/>
<feMergeNode in="blur50"/>
</feMerge>
<feColorMatrix result="yellow-blur" in="blur-merged" type="matrix"
values="0 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.7 0" />
<feMerge>
<feMergeNode in="yellow-blur"/>
<feMergeNode in="blur5"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<image xlink:href="https://i.stack.imgur.com/PBRad.jpg" width="100%" height="100%" />
<path id="star_Full" fill="#E4F6A3" filter="url(#glow)" opacity="0" d="M580.2 76.2 837.2 71.3 774.7 5.7 845.1 62.7 850.1 8c0 0 6.8 54.2 6 54.7-4.3 2.8 70.9-57.5 70.9-57.5L862.8 71.2 1131.2 76.2 864.4 81.8 982.3 203C960.8 189.7 856.8 91.3 856.7 91.2L849.8 194.2 845.7 91.1 714.3 203.7 832.8 82.5Z">
<animate
id="an_star"
attributeName="opacity"
dur="1.5s"
begin="1s;pause_star.end"
values="1;1;0"
fill="freeze"
repeatCount="5" />
<animate
id="pause_star"
attributeName="opacity"
dur="1.5s"
begin="an_star.end"
values="0;0"
fill="freeze"
repeatCount="1" />
</path>
<svg>
От одного участника может быть от одного до трех ответов, в отдельных постах.
Добавлено объявление в топик-приглашение к конкурсу
Салют в лесу
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<count; j++, i++){
g = g.flat().map(s => {
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;
}
<canvas id=fireworksCanvas ></canvas>
<canvas id=forestCanvas ></canvas>
Осторожно, тут нужна видеокарта.
let toy = new ShaderToy(`
// перевод из палитры hsl в палитру rgb
vec3 hsl2rgb(vec3 c) {
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),6.0)-3.0)-1.0, 0.0, 1.0);
return c.z + c.y * (rgb-0.5)*(1.0-abs(2.0*c.z-1.0));
}
// генератор псевдослучайных чисел
float random (vec2 p) {
return fract(sin(dot(p,vec2(12.9898,78.233)))*43758.5453123);
}
// один залп
vec4 firework(vec2 p, float n) {
float dur = 3000.; // время анимации одного фейерверка
float id = floor(time/dur-n) + n*8.; // число - идентификатор залпа
float t = smoothstep(0., 1., fract(time/dur-n)); // время анимации
float t1 = max(0.0, 0.5 - t); // время второй фазы (взрыва)
float t2 = max(0.0, t - 0.5); //
p.y += t1; // первую половину времени летим снизу вверх
p.y -= random(vec2(n*35.+id, n*45.+id))*0.3; // немного по оси y на случ. величину
p.x += n - 0.5 + mix(0., sin(id)*0.4, t1); // немного по оси x на случ.
vec4 c;
if ( dot(p,p) > 0.002 + t2 *0.1 ) // если пиксель слишком далеко
return c;
// цвет частицы
vec3 rgb = hsl2rgb(vec3(id*0.3, .8, .7));
for (float i = 0.; i < 77.; i += 1.) {
// угол отлета отлёта
float angle = i+sin(i*1234. + t);
// дистанция отлёта
float dist = 0.2 + 0.2 * random(vec2(i*351. + id, i*135. + id));
// конечная точка частицы
vec2 pt = p + vec2(dist*sin(angle), dist*cos(angle));
// находим интерполяцией текущее положение
pt = mix(p, pt, t2);
// радиус частицы
float r = .03 * (1. - t) * t2 +
.002*t*t*(1. - max(.0, t - .9)*10.);
// яркость пикселя
float d = 1. - smoothstep(sqrt(dot(pt, pt)), .0, r);
if (t>0.75 && fract(id/3.)<0.3)
d *= 0.6 + sin(111.*(i+id*88.+t))*0.4;
c += vec4(rgb, 1.) * d;
}
return c;
}
void main(void) {
vec2 uv = gl_FragCoord.xy/resolution - 0.5;
uv.x *= resolution.x/resolution.y;
for (float n = 0.; n < 6.; n += 1.)
gl_FragColor += firework(uv, n/6.) - 0.05; // рисуем несколько фейерверков
}`);
addEventListener("resize", () => toy.resize(innerWidth, innerHeight));
toy.resize(innerWidth, innerHeight);
requestAnimationFrame(draw);
function draw(t) {
requestAnimationFrame(draw);
toy.draw(t)
}
canvas {
background: url('https://i.stack.imgur.com/PBRad.jpg');
background-size: cover;
position: fixed;
top: 0;
left: 0;
}
<script src="https://raw.githack.com/strangerintheq/ShaderToy/master/ShaderToy.js"></script>
PS: если кому-то будет интересно как это работает - спрашивайте...
Для затравки первая работа :)
Осторожно, при запуске включается музыкальное сопровождение.
Используется трек Дискотека Авария - Новогодняя
Это было только начало сюжета. Полную версию анимации смотрите в этом же - топике
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet" >
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
</defs>
<!-- Изображение Ёлочки -->
<image xlink:href="https://i.stack.imgur.com/PBRad.jpg" width="100%" height="100%" />
<path id="path1" d="m1648.6 24.4c0 0-341.5 179.4-456.4 327.2-102.2 131.4-97.5 174.6-169 242.2-125.1 118.5-275.2 220.3-439.5 272.1-61.6 19.4-193.6 11-193.6 11v0" style="fill:none;stroke-opacity:0.9;stroke-width:2;stroke:none"/>
<image id="fly" xlink:href="https://i.stack.imgur.com/qnNmf.png" width="2%" height="2%" opacity="1" >
<animateMotion
id="MotionHal"
begin="btn.click+8s"
dur="14s"
fill="freeze"
rotate="auto-reverse"
repeatCount="1" >
<mpath xlink:href="#path1" />
</animateMotion>
<animateTransform id="an_fly" attributeName="transform" type="scale" begin="MotionHal.begin" dur="14s" values="1;2;2;2;4;6;8;9" fill="freeze" repeatCount="1" />
</image>
<!-- Анимация зайчика с гитарой -->
<image id="zayka" filter="url(#shadow)" transform="translate(1050 750) scale(1 1)" x="0" xlink:href="https://i.stack.imgur.com/3xzEW.png" height="20vh"
opacity="0" >
<!-- Появление зайца -->
<animate id="zayka_Hide" attributeName="opacity" begin="btn.click+8s" dur="1s" to="1" fill="freeze" />
<animateTransform id="zayka_Up" attributeName="transform" type="translate" values="1070 750;1075 730;1080 710;1075 730;1070 750" dur="0.5s" begin="zayka_Hide.end" repeatCount="indefinite" />
<animate attributeName="x" begin="zayka_Hide.end" dur="2.5s" values="0;50;70;100;70;50;50;0" repeatCount="indefinite" />
</image>
<!-- Анимация текста Новый Год! -->
<path id="NY_path" d="m1207.8 682.9c0 0 24.8-274.1 4.6-497.1C1193.5-23.4 777.1 18.3 579.3 97.4 360.1 185.1 336 782.2 139.1 652.1-3.1 558.3 41.8 317.2 53.1 95.6c3.9-75.7 19.5 7.8 250.5 6.4 235.6-1.5 261-34.5 261-34.5" style="fill:none;stroke:none"/>
<text text-anchor="middle" font-size="52" font-weght="900" fill="gold" stroke="white" stroke-width="1" filter="url(#shadow)" opacity="0" >
<textPath id="result" method="align" spacing="auto" startOffset="0%" xlink:href="#NY_path"><tspan dx="0" dy="-2">С Новым 2020 годом!</tspan>
<!-- Движение фразы вдоль кривой линии begin ="80s" -->
<animate id="NY_move"
begin ="btn.click+10s"
dur="10s"
repeatCount="1"
attributeName="startOffset"
values="0%;9%;9%;9%;73%;73%;73%;92%;92%"
fill="freeze"/>
</textPath>
<!-- Появление Нового Года -->
<!--NY_move.begin-->
<animate attributeName="opacity" begin="NY_move.begin" dur="0.1s" values="0;1" fill="freeze" />
<!-- Перемещение текста-->
<animateTransform id="text_Up2" attributeName="transform" type="translate" values="0 0;120 20;-40 -20;120 60;-40 60;120 0;-20 80;120 80;0 0" dur="3.5s" begin="NY_move.end;98.5s;106;114.5s" repeatCount="1" />
<!-- Увеличение текста-->
<animateTransform id="text_scale" attributeName="transform" type="scale" calcMode="discrete" values="1;1.2;1.3;1.4;1.5;2;2;2;2;1.5;1.4;1.3;1.2;1" dur="5s" begin="text_Up2.end" repeatCount="1" />
<set id="red" attributeName="fill" to="red" begin="28.5s" />
<set id="lime" attributeName="fill" to="lime" begin="32s" />
<set id="purple" attributeName="fill" to="purple" begin="36.5s" />
<animateTransform id="Scale_purple" attributeName="transform" type="scale" dur="1.5s" from="1" to="2.5" begin="36.5s" fill="freeze" />
<animateTransform id="T_skew" attributeName="transform" attributeType="XML"
type="skewY" values="1;-1.4;1" additive="sum"
begin="Scale_purple.end;T2_skew.end" dur="0.8s" fill="freeze" repeatCount="3"/>
<animateTransform id="T2_skew" attributeName="transform" attributeType="XML"
type="skewY" values="-4;1;-4" additive="sum"
begin="T_skew.end" dur="1.5s" fill="freeze" repeatCount="2"/>
</text>
<g id="btn" onclick='play()' >
<rect x="5" y="10" rx="15" id="rec1" width="100px" height="40px" fill="#4975B2" />
<text x="23" y="40" font-size="32" fill="white"> Start </text>
</g>
</svg>
</div>
<script>
var zodiac = new Audio();
zodiac.src = 'https://svg-art.ru/files/diskoteka_avariya_novogodnyaya.mp3';
function play() {
zodiac.play();
}
</script>
Сырая версия 1.0, в идеале надо добавить еще пару сценок и доработать создание ёлки йолки:
let img = new Image();
img.onload = startScene;
img.onerror = () => console.log("Что-то пошло не так");
img.src = "https://images.vexels.com/media/users/3/157970/isolated/preview/c156b4270aea292b9b335dd463ea17eb-earth-planet-icon-earth-icon-by-vexels.png";
/* Каждую сценку писал отдельно в песочнице.
Показалось легче завернуть каждую в свою функцию, а не заваливать в кучу,
чтобы в будущем легче было переносить-редактировать. */
function startScene() {
let main = document.getElementById('main');
(function enteringEarth() {
let time = 15;
setCSS(`
.scene {
background-color: #001;
}
.earth {
position: absolute;
width: 15px;
animation: closeup ${time}s linear forwards;
}
@keyframes closeup {
0% { width: 15px; }
100% { width: 250px; }
}
.rocket {
fill: white;
transition: ${time + 5}s;
}
`);
setHTML(`
<svg class="scene" width="100%" height="100%">
<image class="earth" x="150" y="150" href="https://images.vexels.com/media/users/3/157970/isolated/preview/c156b4270aea292b9b335dd463ea17eb-earth-planet-icon-earth-icon-by-vexels.png"></image>
<circle class="rocket" cx="200" cy="-5" r="0.5"/>
<circle class="rocket" cx="250" cy="-25" r="0.5"/>
</svg>
`);
setTimeout(() => {
main.querySelectorAll('.rocket').forEach( rocket => {
setAttributes(rocket, {
'cx': 190,
'cy': 190,
'r': 1,
});
});
setTimeout(enteringEarth_closerView, time*1000);
}, 0);
})();
function enteringEarth_closerView() {
setCSS(`
.scene {
background-color: #001;
}
.earth {
fill: #124;
filter: url(#earth-shadow);
}
.rocket-head {
width: 11px;
height: 5px;
fill: url(#rocket-light);
}
.rocket-top-1 {
width: 15px;
height: 2px;
fill: url(#rocket-light);
}
.rocket-top-2 {
width: 15px;
height: 10px;
fill: url(#rocket-dark);
}
.rocket-body {
width: 15px;
height: 140px;
fill: url(#rocket-light);
}
.rocket-foot {
width: 3px;
height: 18px;
fill: #111;
}
.rocket.first {
position: absolute;
animation: rocket_1 6s linear forwards;
}
@keyframes rocket_1 {
0% { transform: rotate(0deg) scale(2); left: 60%; top: 5%; }
84% { transform: rotate(360deg) scale(0.9); left: 40%; top: 50%; }
100% { transform: rotate(400deg) scale(0.9); left: 40%; top: 50%; }
}
.line {
opacity: 0;
stroke: #fff5;
stroke-width: 1px;
stroke-dasharray: 1;
animation: spray 2s linear;
animation-delay: 4s;
}
@keyframes spray {
0% { opacity: 1; }
40% { opacity: 1; }
45% { opacity: 0; }
55% { opacity: 0; }
60% { opacity: 1; }
100% { opacity: 0; }
}
.rocket.second {
position: absolute;
animation: rocket_2 6s linear forwards;
}
@keyframes rocket_2 {
0% { transform: rotateZ(-30deg) rotateX(20deg) scale(1.5); left: 80%; top: 5%}
100% { transform: rotateZ(-40deg) rotateX(30deg) scale(0.9); left: 40%; top: 50%}
}
`);
setHTML(`
<svg class="scene" width="100%" height="100%">
<defs>
<linearGradient id="rocket-light">
<stop offset="0" stop-color="#666"></stop>
<stop offset="40%" stop-color="#bbb"></stop>
<stop offset="50%" stop-color="#ccc"></stop>
<stop offset="60%" stop-color="#bbb"></stop>
<stop offset="100%" stop-color="#666"></stop>
</linearGradient>
<linearGradient id="rocket-dark">
<stop offset="0" stop-color="#111"></stop>
<stop offset="40%" stop-color="#444"></stop>
<stop offset="50%" stop-color="#555"></stop>
<stop offset="60%" stop-color="#444"></stop>
<stop offset="100%" stop-color="#111"></stop>
</linearGradient>
<filter id="earth-shadow">
<feDropShadow dx="-2" dy="-2" stdDeviation="100" flood-color="#045acf"/>
</filter>
</defs>
<circle class="earth" cx="80vw" cy="calc(800vw + 80vh)" r="800vw"/>
</svg>
<svg class="rocket second" width="39px" height="163px">
<rect class="rocket-head" x="12px"/>
<rect class="rocket-top-1" x="10" y="5px"/>
<rect class="rocket-top-2" x="10" y="7px"/>
<rect class="rocket-body" x="10" y="17px"/>
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<rect class="rocket-foot" y="138px" x="12px"/>
<rect class="rocket-foot" y="138px" x="21px"/>
</svg>
<svg class="rocket first" width="39px" height="163px">
<rect class="rocket-head" x="12px"/>
<rect class="rocket-top-1" x="10" y="5px"/>
<rect class="rocket-top-2" x="10" y="7px"/>
<rect class="rocket-body" x="10" y="17px"/>
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<rect class="rocket-foot" y="138px" x="12px"/>
<rect class="rocket-foot" y="138px" x="21px"/>
<g class="spray">
<line class="line" x1="26" y1="15" x2="38" y2="13"/>
<line class="line" x1="26" y1="15" x2="39" y2="14"/>
<line class="line" x1="26" y1="15" x2="40" y2="15"/>
<line class="line" x1="26" y1="15" x2="39" y2="16"/>
<line class="line" x1="26" y1="15" x2="38" y2="17"/>
</g>
</svg>
`);
setTimeout(skyViewBeforeLanding,5900);
}
function skyViewBeforeLanding(){
setCSS(`
.scene {
background-color: #66c5ff;
}
.rocket-head {
width: 11px;
height: 5px;
fill: url(#rocket-light);
}
.rocket-top-1 {
width: 15px;
height: 2px;
fill: url(#rocket-light);
}
.rocket-top-2 {
width: 15px;
height: 10px;
fill: url(#rocket-dark);
}
.rocket-body {
width: 15px;
height: 140px;
fill: url(#rocket-light);
}
.rocket-feet {
stroke: #111;
stroke-width: 2px;
fill: none;
}
.rocket text {
fill: black;
font-size: 13px;
}
.main-fire {
fill: #ffff76;
animation: rocket-fire 0.1s linear infinite;
}
@keyframes rocket-fire {
0% { transform: scaleY(0.80); fill: #ffcc76; }
}
`);
setHTML(`
<svg class="scene" width="100%" height="100%">
<defs>
<linearGradient id="rocket-light">
<stop offset="0" stop-color="#666"></stop>
<stop offset="40%" stop-color="#bbb"></stop>
<stop offset="50%" stop-color="#ccc"></stop>
<stop offset="60%" stop-color="#bbb"></stop>
<stop offset="100%" stop-color="#666"></stop>
</linearGradient>
<linearGradient id="rocket-dark">
<stop offset="0" stop-color="#111"></stop>
<stop offset="40%" stop-color="#444"></stop>
<stop offset="50%" stop-color="#555"></stop>
<stop offset="60%" stop-color="#444"></stop>
<stop offset="100%" stop-color="#111"></stop>
</linearGradient>
</defs>
<g class="rocket first">
<rect class="rocket-head" x="12px" />
<rect class="rocket-top-1" x="10" y="5px" />
<rect class="rocket-top-2" x="10" y="7px" />
<rect class="rocket-body" x="10" y="17px" />
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<g class="rocket-feet" transform="translate(0,142)">
<polygon points="10,0 -6,22 -2,22 10,13"></polygon>
<polygon points="16,1 18,1 19,26 15,26"></polygon>
<polygon points="25,0 25,13 37,22 41,22"></polygon>
</g>
<g transform="translate(14 40)">
<text x="0" y="0">
<tspan x="0" dy="1.2em">S</tspan>
<tspan x="0" dy="1.2em">A</tspan>
<tspan x="0" dy="1.2em">N</tspan>
<tspan x="0" dy="1.2em">T</tspan>
<tspan x="0" dy="1.2em">A</tspan>
<tspan x="0" dy="1.2em" fill="#045acf">X</tspan>
</text>
</g>
<g class="engine-fire" transform="translate(10 163)">
<polygon class="main-fire" points="1,0 14,0 16,10 8,70 -1,10"></polygon>
</g>
</g>
<g class="rocket second">
<rect class="rocket-head" x="12px" />
<rect class="rocket-top-1" x="10" y="5px" />
<rect class="rocket-top-2" x="10" y="7px" />
<rect class="rocket-body" x="10" y="17px" />
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<g class="rocket-feet" transform="translate(0,142)">
<polygon points="10,0 -6,22 -2,22 10,13"></polygon>
<polygon points="16,1 18,1 19,26 15,26"></polygon>
<polygon points="25,0 25,13 37,22 41,22"></polygon>
</g>
<g class="engine-fire" transform="translate(10 163)">
<polygon class="main-fire" points="1,0 14,0 16,10 8,70 -1,10"></polygon>
</g>
</g>
</svg>
`);
let wid = window.innerWidth;
let hei = window.innerHeight;
let rocket = main.querySelectorAll('.rocket');
let coors = [
{x: wid*0.3, y: hei*0.5 - 50},
{x: wid*0.3 + 150, y: hei*0.5 - 90},
];
let dir = {
x: 0.5,
y: 0.8
};
let count = 0;
setRocketCoors(0.1, 150);
let interval = setInterval(function(){
setRocketCoors(0.1, 150);
if( ++count == 140 ){
clearInterval(interval);
coors = [
{x: wid*0.3, y: hei*0.5 - 50},
{x: wid*0.3 + 150, y: hei*0.5 - 90},
];
interval = setInterval(function(){
setRocketCoors(0.4);
}, 20);
}
}, 50);
function setRocketCoors(scale, distance = 0){
rocket[0].setAttribute('transform', `
translate(${(coors[0].x += dir.x)} ${(coors[0].y += dir.y)})
rotate(-35)
scale(${scale})
`);
rocket[1].setAttribute('transform', `
translate(${(coors[1].x += dir.x) - distance} ${(coors[1].y += dir.y)})
rotate(-35)
scale(${scale*0.8})
`);
}
setTimeout(function(){
clearInterval(interval);
landing();
}, 11000);
}
function landing(){
setCSS(`
.scene {
background-color: #66c5ff;
}
.ground {
fill: #aaa;
}
.perspective {
stroke: #666;
}
.rocket-head {
width: 11px;
height: 5px;
fill: url(#rocket-light);
}
.rocket-top-1 {
width: 15px;
height: 2px;
fill: url(#rocket-light);
}
.rocket-top-2 {
width: 15px;
height: 10px;
fill: url(#rocket-dark);
}
.rocket-body {
width: 15px;
height: 140px;
fill: url(#rocket-light);
}
.rocket-feet {
stroke: #111;
stroke-width: 2px;
fill: none;
}
.rocket text {
fill: black;
font-size: 13px;
}
.main-fire {
fill: #ffff76;
animation: rocket-fire 0.1s linear infinite;
}
@keyframes rocket-fire {
0% { transform: scaleY(0.80); fill: #ffcc76; }
}
`);
setHTML(`
<svg class="scene" width="100%" height="100%">
<g class="land">
<rect class="ground" y="50%" width="100%" height="50%" />
<line class="perspective" x1="0" y1="80%" x2="48%" y2="50%" />
<line class="perspective" x1="100%" y1="80%" x2="52%" y2="50%" />
</g>
<defs>
<linearGradient id="rocket-light">
<stop offset="0" stop-color="#666"></stop>
<stop offset="40%" stop-color="#bbb"></stop>
<stop offset="50%" stop-color="#ccc"></stop>
<stop offset="60%" stop-color="#bbb"></stop>
<stop offset="100%" stop-color="#666"></stop>
</linearGradient>
<linearGradient id="rocket-dark">
<stop offset="0" stop-color="#111"></stop>
<stop offset="40%" stop-color="#444"></stop>
<stop offset="50%" stop-color="#555"></stop>
<stop offset="60%" stop-color="#444"></stop>
<stop offset="100%" stop-color="#111"></stop>
</linearGradient>
</defs>
<g class="rocket first">
<rect class="rocket-head" x="12px" />
<rect class="rocket-top-1" x="10" y="5px" />
<rect class="rocket-top-2" x="10" y="7px" />
<rect class="rocket-body" x="10" y="17px" />
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<g class="rocket-feet" transform="translate(0,142)">
<polygon points="10,0 -6,22 -2,22 10,13"></polygon>
<polygon points="16,1 18,1 19,26 15,26"></polygon>
<polygon points="25,0 25,13 37,22 41,22"></polygon>
</g>
<g transform="translate(14 40)">
<text x="0" y="0">
<tspan x="0" dy="1.2em">S</tspan>
<tspan x="0" dy="1.2em">A</tspan>
<tspan x="0" dy="1.2em">N</tspan>
<tspan x="0" dy="1.2em">T</tspan>
<tspan x="0" dy="1.2em">A</tspan>
<tspan x="0" dy="1.2em" fill="#045acf">X</tspan>
</text>
</g>
<g class="engine-fire" transform="translate(10 163)">
<polygon class="main-fire" points="1,0 14,0 16,10 8,70 -1,10"></polygon>
</g>
</g>
<g class="rocket second">
<rect class="rocket-head" x="12px" />
<rect class="rocket-top-1" x="10" y="5px" />
<rect class="rocket-top-2" x="10" y="7px" />
<rect class="rocket-body" x="10" y="17px" />
<polygon points="9,157 26,157 28,163 7,163"></polygon>
<g class="rocket-feet" transform="translate(0,142)">
<polygon points="10,0 -6,22 -2,22 10,13"></polygon>
<polygon points="16,1 18,1 19,26 15,26"></polygon>
<polygon points="25,0 25,13 37,22 41,22"></polygon>
</g>
<g class="engine-fire" transform="translate(10 163)">
<polygon class="main-fire" points="1,0 14,0 16,10 8,70 -1,10"></polygon>
</g>
</g>
</svg>
`);
let wid = window.innerWidth;
let hei = window.innerHeight;
let rocket = main.querySelectorAll('.rocket');
let coors = [
{x: wid*0.3, y: - 250},
{x: wid*0.3 + 150, y: - 290},
];
let move = 1;
let stop = hei*0.5 - 50;
let landing = stop/2.5;
let tick = 5;
setTimeout(handleRocketCoors, tick);
function handleRocketCoors(){
rocket[0].setAttribute('transform', `
translate(${coors[0].x} ${(coors[0].y += move)})
`);
rocket[1].setAttribute('transform', `
translate(${coors[1].x} ${(coors[1].y += move)})
`);
if( coors[0].y >= landing ){
launchLandingFire();
shakeCamera();
}
if( coors[0].y <= stop ) {
setTimeout(handleRocketCoors, tick += 0.05);
} else {
let fire = main.querySelectorAll('.engine-fire');
[...fire].forEach(el => el.outerHTML = "");
setTimeout( transformAndRollout, 2000 );
}
}
}
function launchLandingFire(){
// ?!
}
function shakeCamera(){
let scene = main.querySelector('.scene');
let count = 0;
let interval = setInterval(function(){
let x = (Math.random() < 0.5 ) ? 1 : -1;
let y = (Math.random() < 0.5 ) ? 1 : -1;
scene.style.transform = `translate(${x}px, ${y}px)`
if( ++count >= 250 ) {
clearInterval(interval);
scene.style.transform = "none";
}
}, 5);
}
function transformAndRollout(){
let rocket = main.querySelectorAll('.rocket');
appendTree(rocket[0]);
appendTree(rocket[1]);
function appendTree(rocket){
let tree = document.createElementNS("http://www.w3.org/2000/svg", 'g');
for( let i = 0; i < 500; i++ ){
let points = getRandomPoints();
let leaf = `
<polygon
class="leaf"
points="${points.from}"
fill="${getRandomGreen()}"
>
<animate
attributeName="points"
begin="10s"
dur="5s"
fill="freeze"
from="${points.from}"
to="${points.to}"/>
</polygon>
`;
tree.insertAdjacentHTML('beforeend', leaf);
}
rocket.appendChild(tree);
}
function getRandomPoints(){
let x1 = rand(15) + 10
let y1 = rand(140);
let x2 = x1;
let y2 = y1 + rand(25) + 7;
let x3 = rand(y2) - y2/2;
let y3 = y2 + y2**0.5;
return {
from: `${x1},${y1} ${x2},${y2} ${x2+1},${y3}`,
to: `${x1},${y1} ${x2},${y2} ${x3},${y3}`
}
}
function getRandomGreen(){
let r = Math.random() * 50;
let g = 2 * r;
let b = Math.random() * 50;
return `RGB(${r}, ${g}, ${b})`;
}
function rand(n){
return Math.floor( Math.random() * n );
}
}
}
function setCSS(str){
document.getElementById('css').textContent = str;
}
function setHTML(str){
document.getElementById('main').innerHTML = str;
}
function setAttributes(elem, obj){
for( let attr in obj ){
elem.setAttribute( attr, obj[attr] );
}
}
html, body { margin: 0; padding: 0; }
#main {
width: 100%;
height: 100vh;
overflow: hidden;
}
<style id="css"></style>
<div id="main"></div>
P.s. впервые работал с SVG... чтение кода может вызвать боль и страдания. Анимировал то в SVG, то на CSS, то JS, "лишь бы работало"))
За основу взят первый пример. Добавлены новые персонажи.
Update 23.12.2019
Update 24.12.2019
На первой минуте ёлка стоит вся в снегу. Работает маска - mask id="msk1"
по контуру ёлки.
Затем с момента времени - 1м. 20 сек., после появления снегурочки начинает работать анимация смены заполнения цвета маски, создавая эффект мерцания гирлянд.
Дождитесь :), на мой взгляд, интересно получилось.
<div class="container">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet" >
<defs>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="4" dy="8" stdDeviation="4"/>
</filter>
<!-- Траектория движения Деда Мороза -->
<path id="path1" d="m1648.6 24.4c0 0-297.1 119.8-421 217.9-78.8 62.4-107.2 182.8-196.5 228.8C866.6 555.9 658.8 532.4 478.3 521.4 243.9 507.1 306.7 176.5 680.3 210c110.3 9.9 224.9-25.6 328 11 87.5 31.1 232 76.1 221.8 168.3-17.4 156.9-281.3 146.3-431 196.6-136 45.8-420 94.4-420 94.4" style="fill:none;stroke-opacity:0.9;stroke-width:2;stroke:none"/>
<!-- Траектория движения снегурочки -->
<path id="Girl_Path" d="m1648.6 24.4c0 0-297.1 119.8-421 217.9-78.8 62.4-107.2 182.8-196.5 228.8C866.6 555.9 658.8 532.4 478.3 521.4 243.9 507.1 306.7 176.5 680.3 210c110.3 9.9 224.9-25.6 328 11 87.5 31.1 224.8 75.6 221.8 168.3-6.3 195.5-306.6 254.1-489.2 324-170.8 65.4-542.7 80.2-542.7 80.2" style="fill:none;stroke-width:2;stroke:#f00"/>
<radialGradient id="RadialGrad"
fx="50%" fy="50%" r="65%"
spreadMethod="pad">
<stop offset="0%" stop-color="#E7D68C" stop-opacity="1"/>
<stop offset="100%" stop-color="#FFFEED" stop-opacity="1" />
</radialGradient>
<filter id="glow" filterUnits="userSpaceOnUse"
x="-50%" y="-50%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" stdDeviation="25" result="blur5"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="30" result="blur10"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="40" result="blur20"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="50" result="blur30"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="70" result="blur50"/>
<feMerge result="blur-merged">
<feMergeNode in="blur10"/>
<feMergeNode in="blur20"/>
<feMergeNode in="blur30"/>
<feMergeNode in="blur50"/>
</feMerge>
<feColorMatrix result="yellow-blur" in="blur-merged" type="matrix"
values="0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 0.7 0" />
<feMerge>
<feMergeNode in="yellow-blur"/>
<feMergeNode in="blur5"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<mask id="msk1">
<rect fill="white" width="100%" height="100%"/>
<path fill="#9939CD" d="m832.5 54.2c-25 19.3-14.5 57.9-13.8 93.8 0.2 11.8-8 22.2-12.5 33.1-2.3 5.7-7.5 17-7.5 17 0 0-8.4 18.5-12 28.1-5.5 15-12.7 29.7-14.5 45.6-1 9.1 3.7 18.2 2.5 27.3-1.7 12.3-11.3 22.5-13.1 34.8-1.1 7.5 1.6 15.1 1.5 22.7-0.1 8.4-2.2 25-2.2 25 0 0-0.6 8.1-2.8 11.4-3.6 5.5-12.9 6-15.6 12-2.2 4.9 1.4 10.7 1.1 16.1-0.5 10-7.2 19.7-5.3 29.5 1 5.1 6.3 8.2 8.3 13.1 1.5 3.6-2.4 15.9-6.7 23.9-2.7 5.2-9.3 4.9-10 9.5-1.4 8.4 12 22.5 12 22.5l-8.9 17.8-4.4 30 1.7 30 9.5 10c1 20.7-2.8 6-8.9 11.1-6.3 6.1 2.1 17.9-0.8 26.1-1.7 4.7-9.2 11.7-9.2 11.7 0 0-5.5 11-4.4 16.7 1.1 5.5 8.9 7.9 10 13.3 1.4 7.1-4 14-5.6 21.1-1 4.8-3.3 9.6-2.5 14.5 0.7 4.1 6 6.9 5.8 11.1-0.2 6.5-8.5 10.4-10 16.7-2.4 9.9 1.1 20.4 1.9 30.6 0.4 4.3 0.5 8.6 1.4 12.8 1.4 7 3 14.1 6.1 20.6 2.7 5.5 4.7 13.4 10.6 15 7 1.9 12.8-8.2 20-8.9 14.8-1.4 29.1 6 43.4 10 8.2 2.3 18.7 2 24.1 7.9 10.5 11.5 10.1 15.7 18 17.4 7.7 1.7 21.5 3.3 28.6-1 7.2-4.3 10-12.8 15.3-17 8.3-6.6 14-3.5 20.7-6.3 6.6-2.7 12.1-7.9 18.9-10 6.4-2 13.3-1.9 20-2.2 7-0.4 15.3 4 21.1 0 5.9-4 4.8-13.5 7.8-20 2.5-5.5 6.5-10.4 8.3-16.1 1.3-4.1 0.5-8.7 1.7-12.8 1.7-5.9 6.7-10.7 7.8-16.7 2-10.6-0.8-21.6-2.2-32.3-0.9-6.4-4.2-12.5-3.9-18.9 0.3-5.8 2.4-11.5 5-16.7 1.8-3.7 6.8-5.9 7.2-10 1.2-10.4-4-22.5-12.2-28.9-4.6-3.6-14.9 2.6-17.2-2.8-5.6-13 23.5-20.9 23.9-35 0.3-8.6-13.3-22.2-13.3-22.2l-5-60.1c0 0-1.6-8.9-1.1-13.3 0.6-5.8 6-11 5-16.7-0.9-4.8-8.8-6.8-8.9-11.7-0.1-6.1 10.2-8.9 10.6-15 0.5-7.8-7.4-13.7-11.1-20.6-4.1-7.6-9.4-14.7-12.2-22.8-1.4-3.9-3.2 0.5-2.2-12.2 0.1-9.5-14.8-18.5-10-26.7 2.5-4.2 11.7 1.8 14.5-2.2 14.2-21.2-16.9-49-17.8-74.5-0.2-4.5 4.3-9.3 2.2-13.3-1.9-3.7-8-2.8-11.1-5.6-3-2.7-5.5-6.2-6.7-10-1.5-5 0-10.4 0-15.6 0-4.4 1.2-9.1 0-13.3-1.4-4.6-5-8.3-7.8-12.2-1.1-1.5-3.1-2.6-3.3-4.4-0.6-4.8 3.9-8.8 5.6-13.3 1.2-3.3 3.9-6.5 3.3-10-0.9-5.5-4.3-12.8-9.8-13.3-1.9-0.2-2.3 4.2-4.2 4-8-1.1-10.4-12.9-12.6-20.7-3.6-26.5-14.5-23.1-15.4-35.2-1.3-25.9 11.6-67.9-9.4-89-8.7-8.8-27.1-11.6-36.9-4z">
<!-- Анимация цветов маски -->
<animate
id="Three_fire"
attributeName="fill"
dur="0.5s"
begin="btn.click+80s;Three_pause.end"
values=" #CD9AE9; white;white;#CD9AE9"
fill="freeze"
repeatCount="10" />
<!-- Пауза анимации цветов маски -->
<animate
id="Three_pause"
attributeName="fill"
dur="5s"
begin="Three_fire.end"
values=" white;white"
fill="freeze"
repeatCount="1" />
</path>
</mask>
</defs>
<!-- Изображение Ёлочки -->
<image mask="url(#msk1)" xlink:href="https://i.stack.imgur.com/PBRad.jpg" height="100%" width="100%"/>
<!-- Луна -->
<g transform="translate(1200 220)">
<g transform="rotate(-20 35.5 35.5)">
<circle cx="35.5" cy="35.5" r="35" stroke="none" fill="url(#RadialGrad)" />
<circle cx="35.5" cy="35.5" r="35" stroke="none" fill="black" >
<animate
id="youngMoon"
attributeName="cx"
values="35.5;-35.5"
begin="btn.click+35s;oldMoon.end+1s"
dur="10s"
fill="freeze" />
<animate id="oldMoon" attributeName="cx" values="105;35.5;" begin="youngMoon.end+1s" dur="10s" fill="freeze" />
</circle>
</g>
</g>
<!-- Звезда на ёлке -->
<path id="star_Full" fill="#E4F6A3" filter="url(#glow)" opacity="0" d="M580.2 76.2 837.2 71.3 774.7 5.7 845.1 62.7 850.1 8c0 0 6.8 54.2 6 54.7-4.3 2.8 70.9-57.5 70.9-57.5L862.8 71.2 1131.2 76.2 864.4 81.8 982.3 203C960.8 189.7 856.8 91.3 856.7 91.2L849.8 194.2 845.7 91.1 714.3 203.7 832.8 82.5Z">
<!-- Анимация opacity звезды -->
<animate
id="an_star"
attributeName="opacity"
dur="2.5s"
begin="btn.click+70s;pause_star.end"
values="1;1;0"
fill="freeze"
repeatCount="5" />
<!-- Пауза анимация opacity звезды -->
<animate
id="pause_star"
attributeName="opacity"
dur="1.5s"
begin="an_star.end"
values="0;0"
fill="freeze"
repeatCount="1" />
<!-- Замена цвета у звезды -->
<animate
id="fill_change_star"
attributeName="fill"
dur="11s"
begin="svg1.click;pause_star.end"
values="#CDE7FB;red;#B34EE9;#15C14E"
fill="freeze"
repeatCount="1" />
</path>
<image id="fly" xlink:href="https://i.stack.imgur.com/qnNmf.png" width="2%" height="2%" opacity="1" >
<!-- Анимация самолёта с Дедом морозом -->
<animateMotion
id="MotionHal"
begin="btn.click+8s"
dur="14s"
fill="freeze"
rotate="auto-reverse"
repeatCount="1" >
<mpath xlink:href="#path1" />
</animateMotion>
<!-- Увеличение самолёта -->
<animateTransform
id="an_fly"
attributeName="transform"
type="scale"
begin="MotionHal.begin"
dur="14s"
values="1;2;2;2;4;6;8;9"
fill="freeze"
repeatCount="1" />
<!-- Исчезновение самолёта -->
<animate
id="fly_hide"
attributeName="opacity"
dur="1.5s"
values="1;0"
begin="an_fly.end+5s"
repeatCount="1"
fill="freeze" />
</image>
<!-- Анимация снеговика -->
<image id="Snowman" transform="translate(950 720) scale(1 1)" x="0" xlink:href="https://i.stack.imgur.com/mbefD.png" width="13%" height="13%" opacity="0" filter="url(#shadow)" >
<!-- Снеговик появляется -->
<animate
id="Snowman_Vis"
attributeName="opacity"
begin="W2_hide_back.end+1s;Snowman_Pause.end"
dur="1.5s"
values="0;1"
repeatCount="1"
fill="freeze" />
<!-- Анимация движения снеговика -->
<animateTransform
id="Snowman_TR"
attributeName="transform"
type="translate"
values="
950 720;
1050 720;
1170 720;
1050 720;
950 720"
dur="2s"
begin="Snowman_Vis.end"
keyTimes="0;0.2;0.6;0.75;1"
repeatCount="5" />
<animate
id="Snowman_Hide"
attributeName="opacity"
begin="Snowman_TR.end+0.5s"
dur="1s"
values="1;0"
repeatCount="1"
fill="freeze" />
<animate
id="Snowman_Pause"
attributeName="opacity"
begin="Snowman_Hide.end+0.5s"
dur="10s"
values="0;0"
repeatCount="1"
fill="freeze" />
</image>
<!-- Анимация зайчика с гитарой -->
<image id="zayka" filter="url(#shadow)" transform="translate(1050 850) scale(1 1)"
x="0" xlink:href=" https://i.stack.imgur.com/3xzEW.png"
height="20vh"
opacity="0" >
<!-- Появление зайца -->
<animate
id="zayka_Hide"
attributeName="opacity"
begin="btn.click+8s"
dur="1s"
to="1"
fill="freeze" />
<!-- Прыжки зайца -->
<animateTransform
id="zayka_Up"
attributeName="transform"
type="translate"
values="1070 850;1075 830;1080 810;1075 830;1070 850"
dur="0.5s"
begin="zayka_Hide.end"
end="W2_Tr.end"
repeatCount="indefinite" />
<!-- Движение зайца -->
<animate
id="zayka_Tr"
attributeName="x"
begin="zayka_Hide.end"
end="W2_Tr.end"
dur="2.5s"
values="0;50;70;100;70;50;50;0"
repeatCount="indefinite" />
<!-- Заяц резко поворачивается к волку -->
<animateTransform
id="zayka_Rotate"
attributeName="transform"
type="scale"
dur="1s"
begin="W2_Tr.end"
values="1 1;-1 1;1 1;-1 1"
additive="sum"
repaeatCount="1"
/>
<!-- Прыжки зайца2 -->
<animateTransform
id="zayka_Up2"
attributeName="transform"
type="translate"
values="1070 850;1075 830;1080 810;1075 830;1070 850"
dur="0.5s"
begin="zayka_Rotate.end+0.5s"
repeatCount="indefinite" />
<!-- Движение зайца2 -->
<animate
id="zayka_Tr2"
attributeName="x"
begin="zayka_Rotate.end+0.5s"
dur="2.5s"
values="0;50;70;100;70;50;50;0"
repeatCount="indefinite" />
</image>
<!-- Анимация текста Новый Год! -->
<path id="NY_path" d="m1207.8 682.9c0 0 24.8-274.1 4.6-497.1C1193.5-23.4 777.1 18.3 579.3 97.4 360.1 185.1 336 782.2 139.1 652.1-3.1 558.3 41.8 317.2 53.1 95.6c3.9-75.7 19.5 7.8 250.5 6.4 235.6-1.5 261-34.5 261-34.5" style="fill:none;stroke:none"/>
<text text-anchor="middle" font-size="52" font-weght="900" fill="gold" stroke="white" stroke-width="1" filter="url(#shadow)" opacity="0" >
<textPath id="result" method="align" spacing="auto" startOffset="0%" xlink:href="#NY_path"><tspan dx="0" dy="-2">С Новым 2020 годом!</tspan>
<!-- Движение фразы вдоль кривой линии begin ="80s" -->
<animate id="NY_move"
begin ="btn.click+10s"
dur="10s"
repeatCount="1"
attributeName="startOffset"
values="0%;9%;9%;9%;73%;73%;73%;92%;92%"
fill="freeze"/>
</textPath>
<!-- Появление Нового Года -->
<animate attributeName="opacity" begin="NY_move.begin" dur="0.1s" values="0;1" fill="freeze" />
<!-- Перемещение текста-->
<animateTransform id="text_Up2" attributeName="transform" type="translate" values="0 0;120 20;-40 -20;120 60;-40 60;120 0;-20 80;120 80;0 0" dur="3.5s" begin="NY_move.end;98.5s;106;114.5s" repeatCount="1" />
<!-- Увеличение текста-->
<animateTransform id="text_scale" attributeName="transform" type="scale" calcMode="discrete" values="1;1.2;1.3;1.4;1.5;2;2;2;2;1.5;1.4;1.3;1.2;1" dur="5s" begin="text_Up2.end" repeatCount="1" />
<set id="red" attributeName="fill" to="red" begin="28.5s" />
<set id="lime" attributeName="fill" to="lime" begin="32s" />
<set id="purple" attributeName="fill" to="purple" begin="36.5s" />
<animateTransform id="Scale_purple" attributeName="transform" type="scale" dur="1.5s" from="1" to="2.5" begin="36.5s" fill="freeze" />
<animateTransform id="T_skew" attributeName="transform" attributeType="XML"
type="skewY" values="1;-1.4;1" additive="sum"
begin="Scale_purple.end;T2_skew.end" dur="0.8s" fill="freeze" repeatCount="3"/>
<animateTransform id="T2_skew" attributeName="transform" attributeType="XML"
type="skewY" values="-4;1;-4" additive="sum"
begin="T_skew.end" dur="1.5s" fill="freeze" repeatCount="2"/>
</text>
<!-- Аватарка Рыжий волк -->
<image id="red_wolf" xlink:href="https://i.stack.imgur.com/4TxIO.png" x="620" y="735" opacity="0"
width="8%" height="8%" >
<animate
id="W_OP"
attributeName="opacity"
dur="2.5s"
values="0;1;1;1;0"
begin="btn.click+0.3s"
repeatCount="3" />
</image>
<!-- Аватарка Рыжий волк2 в углу-->
<image id="red_wolf" xlink:href="https://i.stack.imgur.com/4TxIO.png" width="8%" height="8%" x="60" y="935" opacity="0">
<!-- Появление аватарки волка в углу -->
<animate
id="W1_ava"
attributeName="opacity"
dur="2s"
additive="sum"
values="0;1;1;1;0"
begin="W2_hide_back.end+1s;W1_pausa.end"
repeatCount="3"
fill="freeze" />
<!-- Движениее аватарки -->
<animateTransform
id="W1_Tr"
attributeName="transform"
attributeType="XML"
type="translate" values="0;30;60;60;30;0"
additive="sum"
begin="W1_ava.end-7s"
dur="6s"
fill="freeze"
repeatCount="1"/>
<!-- Пауза аватарки -->
<animate
id="W1_pausa"
attributeName="opacity"
dur="6s"
additive="sum"
values="0;0"
begin="W1_Tr.end"
repeatCount="1"
fill="freeze" />
</image>
<!-- Сидящий волк -->
<image xlink:href="https://i.stack.imgur.com/zFbSq.png" x="50" y="860"
opacity="0" width="20%" height="20%" >
<!-- Появление волка -->
<animate
id="W2_OP"
attributeName="opacity"
dur="1.5s"
values="0;1"
begin="btn.click+20s"
repeatCount="1"
fill="freeze" />
<!-- Волк подпрыгает вверх -->
<animateTransform
id="W2_skew"
attributeName="transform"
attributeType="XML"
type="skewY" values="1;-0.1;1"
begin="W2_OP.end"
dur="0.5s"
fill="freeze"
repeatCount="20"/>
<!-- Волк движется к зайцу -->
<animateTransform
id="W2_Tr"
attributeName="transform"
attributeType="XML"
type="translate" values="0;30;60;90;120;150;180;210;240;270;300;330;360;390;420;450;480;510;540;570"
additive="sum"
begin="W2_OP.end"
dur="11s"
fill="freeze"
repeatCount="1"/>
<!-- Поворот волка от зайца -->
<animateTransform
id="W2_Rotate"
attributeName="transform"
type="scale"
dur="0.5s"
begin="zayka_Rotate.end"
values="1 1;-1 1"
additive="sum"
repaeatCount="1"
fill="freeze" />
<!-- Движение волка после удара -->
<animateTransform
id="W2_back"
attributeName="transform"
attributeType="XML"
type="translate"
values="0;430"
additive="sum"
begin="W2_Rotate.end"
dur="1.5s"
fill="freeze"
repeatCount="1"/>
<!-- Исчезновение волка -->
<animate
id="W2_hide_back"
attributeName="opacity"
dur="1.5s"
additive="sum"
values="0;1"
begin="W2_Rotate.end"
repeatCount="1"
fill="freeze" />
</image>
<!-- Снегурочка -->
<image id="Girl" transform="translate(450 570) scale(1 1)" xlink:href="https://i.stack.imgur.com/RDght.png" width="30%" height="30%" opacity="0">
<!-- Появление Снегурочки -->
<animate
id="Yes_Girl"
attributeName="opacity"
begin="Horse_hide.end+1s"
dur="1.5s"
values="0;1"
repeatCount="1"
fill="freeze" />
<!-- Движения Снегурочки -->
<animateTransform
attributeName="transform"
type="translate"
begin="Yes_Girl.end+1s"
values="
450 550;
580 590;
480 550;
580 590;
530 550;
500 590;
450 550"
dur="2.5s"
repeatCount="indefinite" />
</image>
<!-- Дед мороз -->
<image id="Ded_Moroz" transform="translate(250 550) scale(1 1)" xlink:href="https://i.stack.imgur.com/TYaVo.png" width="30%" height="30%" opacity="0">
<!-- Появление деда Мороза -->
<animate
id="Yes_Ded"
attributeName="opacity"
begin="fly_hide.end+8s"
dur="2.5s"
values="0;1"
repeatCount="1"
fill="freeze" />
<!-- Движения Деда Мороза -->
<animateTransform
id="Ded_TR"
attributeName="transform"
type="translate"
begin="Yes_Ded.end+1s"
values="
250 550;
280 580;
280 550;
300 580;
280 550;
250 580;
250 550"
dur="2s"
repeatCount="indefinite" />
</image>
<!-- Полет снегурочки на автомобиле -->
<image id="flyG" xlink:href="https://i.stack.imgur.com/3Jxdw.png" width="3%" height="3%" opacity="1" >
<!-- Движение автомобиля вдоль пути -->
<!-- begin="btn.click+70s" -->
<animateMotion
id="MotionGirl"
begin="btn.click+45s"
dur="20s"
fill="freeze"
additive="sum"
rotate="auto-reverse"
repeatCount="1" >
<mpath xlink:href="#Girl_Path" />
</animateMotion>
<!-- Увеличение автомобиля -->
<animateTransform
id="Horse_TR"
attributeName="transform"
type="scale"
begin="MotionGirl.begin"
dur="20s"
additive="sum"
values="1.2;1.4;2;3.5;4.5;5.5;8"
fill="freeze"
repeatCount="1" />
<!-- Исчезновение автомобиля -->
<animate xlink:href="#flyG"
id="Horse_hide"
attributeName="opacity"
dur="1.5s"
values="1;0"
begin="Horse_TR.end+4s"
repeatCount="1"
fill="freeze" />
</image>
<g id="btn" onclick='play()' >
<rect x="2" y="3" rx="15" id="rec1" width="100px" height="40px" fill="#4975B2" />
<text x="20" y="33" font-size="32" fill="white"> Start </text>
</g>
</svg>
</div>
<script>
var zodiac = new Audio();
zodiac.src = 'https://svg-art.ru/files/diskoteka_avariya_novogodnyaya.mp3';
function play() {
zodiac.play();
}
</script>
Очень долго раскачивались в этом году потенциальные участники конкурса и видимо просто не рассчитали время или вмешались какие-то неожиданные события, например: предновогодние хлопоты :)))
Не бросайте свои начатые работы, они ещё пригодятся!
Все уже опубликованные ответы, кроме победившей в прошлом конкурсе, также как и новые ответы будут участвовать в новом конкурсе.
Все благодарности @HamSter за продолжение Новогоднего праздника!
Поздравляем победителя Harry Heman!
==========================================================================
Все благодарности @Leks за продолжение Новогоднего праздника с плавным переходом к Рождеству!
==========================================================================
На приглашения принять и опубликовать ответы часто слышу
Но ведь всё это можно реализовать другими средствами.
Все благодарности за идею @A K♦
Не обязательно рисовать, можно взять готовую картинку и подвигать её.
Можно с помощью JS или другого языка, используя символы Юникода или Dos написать код, рисующий анимацию.
Всё зависит от вашей фантазии:
<canvas id ='NewYear'></canvas>
<script>
var db = document.body;
var c = document.getElementById('NewYear');
var $ = c.getContext('2d');
c.width = window.innerWidth;
c.height = window.innerHeight;
var resume;
function relay(){
window.requestAnimationFrame(relay);
resume();
}
</script>
<!--Script to Relay!-->
<script>
//BEGIN SCRIPT RELAY TO ru.StackOverflow.com...
_s = db.querySelectorAll("script")[1].innerHTML.split("\n");
x = 0;
y = 1;
c.width = w = window.innerWidth;
c.height = h = _s.length*20;
db.style.margin = 0;
db.style.background = "hsla(0,0%,0%,1)";
db.style.overflow = "hidden";
$.textBaseline = "top";
$.font = "1.1em monospace";
//PAUSE RELAY: ...
//С НОВЫМ 2020 ГОДОМ!
// УДАЧИ И ВЕЗЕНИЯ ВО ВСЕХ НАЧИНАНИЯХ, КАК В ВИРТУАЛЬНОМ,
// ТАК И В РЕАЛЬНОМ МИРЕ!
/*
★
*o*
*♥*o*
***o***
**o**♥*o*
**♥**o**o**
**o**♥***♥*o*
*****♥*o**o****
**♥**o*****o**♥**
******o*****♥**o***
****o***♥**o***o***♥*
||
\____ ||_____/
╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬
*/
resume = function(){
$.globalCompositeOperation = "source-over";
$.shadowBlur = 0; $.fillStyle = "hsla(0,0%,0%,0.4)";
$.fillRect(0,0,w,h);
$.shadowColor = "hsla(120,100%,50%,0.5)";
$.shadowBlur = 9; $.fillStyle = "hsla(120,100%,20%,1)";
$.globalCompositeOperation = "lighter";
_s.forEach(function(t, i) {
if (i <= y) {
if (i == y) { t = t.substr(0, x); }
$.fillText(t, 100, 100+i*16); }
});
$.fillStyle = "hsla(120,100%,50%,1)";
$.fillRect(100+$.measureText(_s[y].substr(0, x)).width, 102+y*16, 10, 14);
x++;
if (x >= _s[y].length) { y++; x = 0;}
if (y*16 > innerHeight-340) { $.translate(0, -0.5);}
if (y >= _s.length-1) { window.clearInterval();}}
relay();
</script>
Источник Там есть и другие примеры
**Простой пример создания персонажа**Наступает год металлической Крысы.
Нашел в сети рисунок крысы с минимальным количеством деталей.
Техника создания по шагам:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="800" height="500" viewBox="0 0 800 500" preserveAspectRatio="xMinYMin meet">
<image xlink:href="https://i.stack.imgur.com/F9rB3.jpg" width="100%" height="100%" />
</svg>
и наносим по контуру узловые точки
path
в другой файл SVGДобавляем анимацию рисования линии с помощью изменения атрибута stroke-dasharray
для path
На сайте много примеров создания подобных анимаций
<animate id="an_body" attributeName="stroke-dasharray" dur="4s" begin="0s" values="0 2482;2482 0" fill="freeze" />
Анимация готова. Я думаю, при желании её можно будет повторить для других картинок
let eye = document.getElementById("an_eye");
let mustache_1 = document.getElementById("an_mustache_1");
let mustache_2 = document.getElementById("an_mustache_2");
let mustache_3 = document.getElementById("an_mustache_3");
const btn = document.getElementById('btn1');
btn.addEventListener("click", () => {
an_body.beginElement();
eye = fRestartAnim(eye, 3600);
mustache_1 = fRestartAnim(mustache_1, 5000);
mustache_2 = fRestartAnim(mustache_2, 5000);
mustache_3 = fRestartAnim(mustache_3, 5000);
});
function fRestartAnim(an, delay = 0) {
const anClone = an.cloneNode(true);
const anParent = an.parentNode;
anParent.replaceChild(anClone, an);
anClone.setAttribute("begin", performance.now() + delay + "ms");
return anClone;
}
<div><button id="btn1">Click me</button></div>
<svg id="svg1" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="500" viewBox="0 0 800 500" preserveAspectRatio="xMinYMin meet">
<style>
.s0 {
fill: none;
stroke: black;
stroke-width: 5;
}
#mustache_1, #mustache_2, #mustache_3 {
stroke-width: 3;
}
</style>
<rect width="100%" height="100%" fill="#D6DFE6" />
<path id="body" class="s0" stroke-dasharray="0 2482" d="m228.8 142c0 0-1 9.2 1.5 12.7 6.9 9.7 20.4 13.6 32.2 15.7 13.1 2.3 27.1 0.1 39.7-4.1 13.2-4.4 24.9-12.8 36-21.3 5.6-4.3 10.6-9.5 15-15 4.6-5.8 9.8-11.6 12-18.7 2.2-7 3.3-14.4 0.7-22.1C360.6 72.7 351.5 65 340.9 56.1 326.1 43.9 308.2 34.5 289.5 30.3c-19.5-4.4-40.4-2.6-59.9 1.5-20.1 4.2-39.6 12.3-56.9 23.2-24.2 15.3-46.2 35-63.7 57.7-18.9 24.5-34 52.7-42.7 82.4-7.7 26.3-10.4 54.7-7.5 82 4.4 41.8 18.8 62.2 37.5 88.1 11.1 15.3 27 26.8 42.7 37.5 13.2 9 27.5 16.8 42.7 21.7 13.5 4.4 27.8 5.4 41.9 6.7 27 2.5 81.3 2.2 81.3 2.2l74.9-0.7c0 0 33.9-0.2 49.4-1 9.3-0.5 13-14.3 15.7-23.2 2.8-9.1 2.3-19.3 0-28.5-2.7-11-8.6-21.2-15.7-30-6.3-7.8-14.8-13.8-23.2-19.3-12.1-7.8-25.1-14.7-39-18.9-12.5-3.8-25.9-5.4-39-5.2-17.9 0.2-35.9 3.4-53.2 8.2-4.7 1.3-13.4 5.5-13.4 5.5 0 0 13.4-17.7 21.2-25.6 9.3-9.4 19.6-18 30.4-25.7 13-9.1 26.8-17.3 41.2-23.9 13.8-6.3 27.9-12.8 42.9-15 18.2-2.7 37.5-2.3 55.2 2.6 14.2 3.9 27.1 12.3 39 21 10.4 7.6 18.8 17.6 27.7 27 7.9 8.3 13.3 24.3 10.5 36.7-2.1 9.1-10.1 17.4-18.7 21-9 3.8-21.6 4.6-29.2-1.5-8.2-6.7-9.4-20.2-8.2-30.7 1.4-12.3 8.8-23.9 17.2-33 8.4-9 19.4-16.7 31.5-19.5 11.7-2.7 25.2-1.6 36 3.7 8.9 4.4 15.6 13 20.2 21.7 4.6 8.6 6.8 19.4 6.7 28.5 0 9.5-16.5 23.2-16.5 23.2 0 0 19.8 2.8 29.2 6 9.7 3.3 19.2 7.6 27.5 13.5 9.6 6.9 17.6 15.9 24.9 25.2 4.7 5.9 11.9 19 11.9 19l9 18c0 0 0.7 15.9 6.7 18.7 3.9 1.8 11.1-0.3 12-4.5 0.4-1.7-2.1-3-3.7-3.7-2.3-1.1-5.1-1.6-7.5-0.7-3.3 1.2-7.5 7.5-7.5 7.5h-26.2l-43.9 0.9c0 0-14.8-0.4-22.2-0.4-13.2-0.1-26.6-0.3-39.7 0.2-16.9 0.6-34.3-0.6-53.8 0.1-15.7 0.5-51.6 0.5-51.6 0.5" >
<animate id="an_body" attributeName="stroke-dasharray" dur="4s" begin="indefinite" values="0 2482;2482 0" fill="freeze" restart="whenNotActive" />
</path>
<path id="eye" class="s0" stroke-dasharray="0 43" d="m570.4 371.6c0 0 5.3-5.3 8.5-7.2 2.9-1.7 6.1-3.4 9.5-3.7 3.6-0.3 7.5 0.5 10.7 2.3 3.1 1.7 7.4 7.5 7.4 7.5" >
<animate id="an_eye" attributeName="stroke-dasharray" dur="1s" begin="indefinite" values="0 43;43 0" fill="freeze" restart="whenNotActive" />
</path>
<path id="mustache_1" class="s0" stroke-dasharray="0 110" d="m542.9 462c2.5-8 7-15.1 10.7-22.5 2.5-4.9 5.2-9.7 7.7-14.7 1.2-2.5 2.2-5.1 3.6-7.5 0.6-1.1 1.1-2.1 2-3.2 0.2-0.3 0.5 0.6 0.4 0.9-1 4.9-2.3 8.2-3.8 12.2-1.3 3.4-3.1 6.6-4.8 9.8-1.2 2.3-2.4 4.6-3.6 6.9-1.1 2-2.2 4.1-3.3 6.1-2.2 4-4.6 9-6.8 11.8-1.9 2.4-2.3 0.9-2.1 0.3z" >
<animate id="an_mustache_1" attributeName="stroke-dasharray" dur="1s" begin="indefinite"
values="0 110;110 0" fill="freeze" restart="whenNotActive" />
</path>
<path id="mustache_2" class="s0" stroke-dasharray="0 157" d="m589.1 412.5c-1 3.9-0.4 9.7-1.1 14.6-0.8 5.5-2.3 10.9-3.4 16.4-1.2 6-2.7 11.9-3.7 17.9-1.1 6.4-2.4 12.8-2.6 19.3-0.1 1.9-0.3 7.3 0.3 5.6 1.5-5 1.7-7.7 2.6-11.6 1.3-5.6 2.9-11.1 4.3-16.6 1.3-5.2 2.6-10.3 3.5-15.5 0.5-2.8 0.9-5.6 1.2-8.3 0.4-3.7 0.9-7.3 1.1-11 0.2-3.7-0.2-7.1-0.1-11.3 0.1-3.8-1.5-1.6-2 0.5z" >
<animate id="an_mustache_2" attributeName="stroke-dasharray" dur="1s" begin="indefinite"
values="0 157;157 0" fill="freeze" restart="whenNotActive" />
</path>
<path id="mustache_3" stroke-dasharray="0 159" class="s0" d="m608.2 408.4c-0.2 4.1 0.6 6.9 1.3 10.3 0.5 2.4 1.4 4.7 2 7 1.3 5.6 1.8 11.4 3.2 16.9 1.5 6.2 3.7 12.2 5.6 18.3 2.4 7.7 4.1 15.7 7.4 22.9 0.2 0.4 1.3 0.3 1.3-0.1-0.3-5.6-1.2-6.9-1.9-10.3-1.2-6.1-2.3-12.3-3.7-18.4-0.9-4-2-7.9-3.2-11.8-0.9-2.9-2-5.6-2.9-8.5-1-3.1-2.1-6.2-3-9.4-1.6-5.4-2.3-9.6-4.5-16.2-0.2-0.6-1.6-1.4-1.6-0.8z">
<animate id="an_mustache_3" attributeName="stroke-dasharray" dur="1s" begin="indefinite"
values="0 159;159 0" fill="freeze" restart="whenNotActive" />
</path>
</svg>
27.12.2019
Мерцание огней ёлочки
В векторном редакторе, точно также, как в примере выше наносим по контуру ёлочки узловые точки и забираем path
этого контура в другой файл.
Применяем к этому контуру маску с разными цветами, анимируем перебор их, что создаёт иллюзию мерцания гирлянд.
Анимация начинается после клика по заснеженной ёлочке
10 раз моргает, затем пауза 5 сек. и снова моргает
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet">
<defs>
<mask id="msk1">
<rect fill="white" width="100%" height="100%"/>
<path fill="#9939CD" d="m832.5 54.2c-25 19.3-14.5 57.9-13.8 93.8 0.2 11.8-8 22.2-12.5 33.1-2.3 5.7-7.5 17-7.5 17 0 0-8.4 18.5-12 28.1-5.5 15-12.7 29.7-14.5 45.6-1 9.1 3.7 18.2 2.5 27.3-1.7 12.3-11.3 22.5-13.1 34.8-1.1 7.5 1.6 15.1 1.5 22.7-0.1 8.4-2.2 25-2.2 25 0 0-0.6 8.1-2.8 11.4-3.6 5.5-12.9 6-15.6 12-2.2 4.9 1.4 10.7 1.1 16.1-0.5 10-7.2 19.7-5.3 29.5 1 5.1 6.3 8.2 8.3 13.1 1.5 3.6-2.4 15.9-6.7 23.9-2.7 5.2-9.3 4.9-10 9.5-1.4 8.4 12 22.5 12 22.5l-8.9 17.8-4.4 30 1.7 30 9.5 10c1 20.7-2.8 6-8.9 11.1-6.3 6.1 2.1 17.9-0.8 26.1-1.7 4.7-9.2 11.7-9.2 11.7 0 0-5.5 11-4.4 16.7 1.1 5.5 8.9 7.9 10 13.3 1.4 7.1-4 14-5.6 21.1-1 4.8-3.3 9.6-2.5 14.5 0.7 4.1 6 6.9 5.8 11.1-0.2 6.5-8.5 10.4-10 16.7-2.4 9.9 1.1 20.4 1.9 30.6 0.4 4.3 0.5 8.6 1.4 12.8 1.4 7 3 14.1 6.1 20.6 2.7 5.5 4.7 13.4 10.6 15 7 1.9 12.8-8.2 20-8.9 14.8-1.4 29.1 6 43.4 10 8.2 2.3 18.7 2 24.1 7.9 10.5 11.5 10.1 15.7 18 17.4 7.7 1.7 21.5 3.3 28.6-1 7.2-4.3 10-12.8 15.3-17 8.3-6.6 14-3.5 20.7-6.3 6.6-2.7 12.1-7.9 18.9-10 6.4-2 13.3-1.9 20-2.2 7-0.4 15.3 4 21.1 0 5.9-4 4.8-13.5 7.8-20 2.5-5.5 6.5-10.4 8.3-16.1 1.3-4.1 0.5-8.7 1.7-12.8 1.7-5.9 6.7-10.7 7.8-16.7 2-10.6-0.8-21.6-2.2-32.3-0.9-6.4-4.2-12.5-3.9-18.9 0.3-5.8 2.4-11.5 5-16.7 1.8-3.7 6.8-5.9 7.2-10 1.2-10.4-4-22.5-12.2-28.9-4.6-3.6-14.9 2.6-17.2-2.8-5.6-13 23.5-20.9 23.9-35 0.3-8.6-13.3-22.2-13.3-22.2l-5-60.1c0 0-1.6-8.9-1.1-13.3 0.6-5.8 6-11 5-16.7-0.9-4.8-8.8-6.8-8.9-11.7-0.1-6.1 10.2-8.9 10.6-15 0.5-7.8-7.4-13.7-11.1-20.6-4.1-7.6-9.4-14.7-12.2-22.8-1.4-3.9-3.2 0.5-2.2-12.2 0.1-9.5-14.8-18.5-10-26.7 2.5-4.2 11.7 1.8 14.5-2.2 14.2-21.2-16.9-49-17.8-74.5-0.2-4.5 4.3-9.3 2.2-13.3-1.9-3.7-8-2.8-11.1-5.6-3-2.7-5.5-6.2-6.7-10-1.5-5 0-10.4 0-15.6 0-4.4 1.2-9.1 0-13.3-1.4-4.6-5-8.3-7.8-12.2-1.1-1.5-3.1-2.6-3.3-4.4-0.6-4.8 3.9-8.8 5.6-13.3 1.2-3.3 3.9-6.5 3.3-10-0.9-5.5-4.3-12.8-9.8-13.3-1.9-0.2-2.3 4.2-4.2 4-8-1.1-10.4-12.9-12.6-20.7-3.6-26.5-14.5-23.1-15.4-35.2-1.3-25.9 11.6-67.9-9.4-89-8.7-8.8-27.1-11.6-36.9-4z">
<animate
id="Three_fire"
attributeName="fill"
dur="0.5s"
begin="svg1.click;Three_pause.end"
values=" #CD9AE9; white;white;#CD9AE9"
fill="freeze"
repeatCount="10" />
<animate
id="Three_pause"
attributeName="fill"
dur="5s"
begin="Three_fire.end"
values=" white;white"
fill="freeze"
repeatCount="1" />
</path>
</mask>
</defs>
<image mask="url(#msk1)" xlink:href="https://i.stack.imgur.com/PBRad.jpg" height="100%" width="100%"/>
</svg>
Добавляем звезду
Всё сделано, как и в других примерах выше - с помощью векторного редактора рисуем контур и анимируем цвет и прозрачность с помощью SVG фильтров
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet">
<defs>
<filter id="glow" filterUnits="userSpaceOnUse"
x="-50%" y="-50%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" stdDeviation="25" result="blur5"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="30" result="blur10"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="40" result="blur20"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="50" result="blur30"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="70" result="blur50"/>
<feMerge result="blur-merged">
<feMergeNode in="blur10"/>
<feMergeNode in="blur20"/>
<feMergeNode in="blur30"/>
<feMergeNode in="blur50"/>
</feMerge>
<feColorMatrix result="yellow-blur" in="blur-merged" type="matrix"
values="0 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 0.7 0" />
<feMerge>
<feMergeNode in="yellow-blur"/>
<feMergeNode in="blur5"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<mask id="msk1">
<rect fill="white" width="100%" height="100%"/>
<path fill="#9939CD" d="m832.5 54.2c-25 19.3-14.5 57.9-13.8 93.8 0.2 11.8-8 22.2-12.5 33.1-2.3 5.7-7.5 17-7.5 17 0 0-8.4 18.5-12 28.1-5.5 15-12.7 29.7-14.5 45.6-1 9.1 3.7 18.2 2.5 27.3-1.7 12.3-11.3 22.5-13.1 34.8-1.1 7.5 1.6 15.1 1.5 22.7-0.1 8.4-2.2 25-2.2 25 0 0-0.6 8.1-2.8 11.4-3.6 5.5-12.9 6-15.6 12-2.2 4.9 1.4 10.7 1.1 16.1-0.5 10-7.2 19.7-5.3 29.5 1 5.1 6.3 8.2 8.3 13.1 1.5 3.6-2.4 15.9-6.7 23.9-2.7 5.2-9.3 4.9-10 9.5-1.4 8.4 12 22.5 12 22.5l-8.9 17.8-4.4 30 1.7 30 9.5 10c1 20.7-2.8 6-8.9 11.1-6.3 6.1 2.1 17.9-0.8 26.1-1.7 4.7-9.2 11.7-9.2 11.7 0 0-5.5 11-4.4 16.7 1.1 5.5 8.9 7.9 10 13.3 1.4 7.1-4 14-5.6 21.1-1 4.8-3.3 9.6-2.5 14.5 0.7 4.1 6 6.9 5.8 11.1-0.2 6.5-8.5 10.4-10 16.7-2.4 9.9 1.1 20.4 1.9 30.6 0.4 4.3 0.5 8.6 1.4 12.8 1.4 7 3 14.1 6.1 20.6 2.7 5.5 4.7 13.4 10.6 15 7 1.9 12.8-8.2 20-8.9 14.8-1.4 29.1 6 43.4 10 8.2 2.3 18.7 2 24.1 7.9 10.5 11.5 10.1 15.7 18 17.4 7.7 1.7 21.5 3.3 28.6-1 7.2-4.3 10-12.8 15.3-17 8.3-6.6 14-3.5 20.7-6.3 6.6-2.7 12.1-7.9 18.9-10 6.4-2 13.3-1.9 20-2.2 7-0.4 15.3 4 21.1 0 5.9-4 4.8-13.5 7.8-20 2.5-5.5 6.5-10.4 8.3-16.1 1.3-4.1 0.5-8.7 1.7-12.8 1.7-5.9 6.7-10.7 7.8-16.7 2-10.6-0.8-21.6-2.2-32.3-0.9-6.4-4.2-12.5-3.9-18.9 0.3-5.8 2.4-11.5 5-16.7 1.8-3.7 6.8-5.9 7.2-10 1.2-10.4-4-22.5-12.2-28.9-4.6-3.6-14.9 2.6-17.2-2.8-5.6-13 23.5-20.9 23.9-35 0.3-8.6-13.3-22.2-13.3-22.2l-5-60.1c0 0-1.6-8.9-1.1-13.3 0.6-5.8 6-11 5-16.7-0.9-4.8-8.8-6.8-8.9-11.7-0.1-6.1 10.2-8.9 10.6-15 0.5-7.8-7.4-13.7-11.1-20.6-4.1-7.6-9.4-14.7-12.2-22.8-1.4-3.9-3.2 0.5-2.2-12.2 0.1-9.5-14.8-18.5-10-26.7 2.5-4.2 11.7 1.8 14.5-2.2 14.2-21.2-16.9-49-17.8-74.5-0.2-4.5 4.3-9.3 2.2-13.3-1.9-3.7-8-2.8-11.1-5.6-3-2.7-5.5-6.2-6.7-10-1.5-5 0-10.4 0-15.6 0-4.4 1.2-9.1 0-13.3-1.4-4.6-5-8.3-7.8-12.2-1.1-1.5-3.1-2.6-3.3-4.4-0.6-4.8 3.9-8.8 5.6-13.3 1.2-3.3 3.9-6.5 3.3-10-0.9-5.5-4.3-12.8-9.8-13.3-1.9-0.2-2.3 4.2-4.2 4-8-1.1-10.4-12.9-12.6-20.7-3.6-26.5-14.5-23.1-15.4-35.2-1.3-25.9 11.6-67.9-9.4-89-8.7-8.8-27.1-11.6-36.9-4z">
<animate
id="Three_fire"
attributeName="fill"
dur="0.5s"
begin="svg1.click;Three_pause.end"
values=" #CD9AE9; white;white;#CD9AE9"
fill="freeze"
repeatCount="10" />
<animate
id="Three_pause"
attributeName="fill"
dur="5s"
begin="Three_fire.end"
values=" white;white"
fill="freeze"
repeatCount="1" />
</path>
</mask>
</defs>
<image mask="url(#msk1)" xlink:href="https://i.stack.imgur.com/PBRad.jpg" height="100%" width="100%"/>
<path id="star_Full" fill="#CDE7FB" filter="url(#glow)" opacity="0" d="M580.2 76.2 837.2 71.3 774.7 5.7 845.1 62.7 850.1 8c0 0 6.8 54.2 6 54.7-4.3 2.8 70.9-57.5 70.9-57.5L862.8 71.2 1131.2 76.2 864.4 81.8 982.3 203C960.8 189.7 856.8 91.3 856.7 91.2L849.8 194.2 845.7 91.1 714.3 203.7 832.8 82.5Z">
<animate
id="an_star"
attributeName="opacity"
dur="2.5s"
begin="svg1.click;pause_star.end"
values="1;1;0"
fill="freeze"
repeatCount="5" />
<animate
id="pause_star"
attributeName="opacity"
dur="1.5s"
begin="an_star.end"
values="0;0"
fill="freeze"
repeatCount="1" />
</path>
</svg>
Добавляем дополнительно анимацию замены цвета для основы фильтра.
В результате получаем иллюзию случайно выбранного цвета для звезды
Так как длительность циклов замены основного цвета звезды, циклы применения фильтров, циклы применения масок, паузы имеют разные значения.
<animate
id="fill_change_star"
attributeName="fill"
dur="20s"
begin="svg1.click"
values="#CDE7FB;yellowgreen;crimson;#CDE7FB"
fill="freeze"
repeatCount="1" />
Анимация начнется после клика по ёлочке
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%" height="100%" viewBox="0 0 1680 1050" preserveAspectRatio="xMinYMin meet">
<defs>
<filter id="glow" filterUnits="userSpaceOnUse"
x="-50%" y="-50%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" stdDeviation="25" result="blur5"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="30" result="blur10"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="40" result="blur20"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="50" result="blur30"/>
<feGaussianBlur in="SourceGraphic" stdDeviation="70" result="blur50"/>
<feMerge result="blur-merged">
<feMergeNode in="blur10"/>
<feMergeNode in="blur20"/>
<feMergeNode in="blur30"/>
<feMergeNode in="blur50"/>
</feMerge>
<feColorMatrix result="yellow-blur" in="blur-merged" type="matrix"
values="0 0 0 0 0
0 1 0 0 0
0 0 0 0 0
0 0 0 0.7 0" />
<feMerge>
<feMergeNode in="yellow-blur"/>
<feMergeNode in="blur5"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<mask id="msk1">
<rect fill="white" width="100%" height="100%"/>
<path fill="#9939CD" d="m832.5 54.2c-25 19.3-14.5 57.9-13.8 93.8 0.2 11.8-8 22.2-12.5 33.1-2.3 5.7-7.5 17-7.5 17 0 0-8.4 18.5-12 28.1-5.5 15-12.7 29.7-14.5 45.6-1 9.1 3.7 18.2 2.5 27.3-1.7 12.3-11.3 22.5-13.1 34.8-1.1 7.5 1.6 15.1 1.5 22.7-0.1 8.4-2.2 25-2.2 25 0 0-0.6 8.1-2.8 11.4-3.6 5.5-12.9 6-15.6 12-2.2 4.9 1.4 10.7 1.1 16.1-0.5 10-7.2 19.7-5.3 29.5 1 5.1 6.3 8.2 8.3 13.1 1.5 3.6-2.4 15.9-6.7 23.9-2.7 5.2-9.3 4.9-10 9.5-1.4 8.4 12 22.5 12 22.5l-8.9 17.8-4.4 30 1.7 30 9.5 10c1 20.7-2.8 6-8.9 11.1-6.3 6.1 2.1 17.9-0.8 26.1-1.7 4.7-9.2 11.7-9.2 11.7 0 0-5.5 11-4.4 16.7 1.1 5.5 8.9 7.9 10 13.3 1.4 7.1-4 14-5.6 21.1-1 4.8-3.3 9.6-2.5 14.5 0.7 4.1 6 6.9 5.8 11.1-0.2 6.5-8.5 10.4-10 16.7-2.4 9.9 1.1 20.4 1.9 30.6 0.4 4.3 0.5 8.6 1.4 12.8 1.4 7 3 14.1 6.1 20.6 2.7 5.5 4.7 13.4 10.6 15 7 1.9 12.8-8.2 20-8.9 14.8-1.4 29.1 6 43.4 10 8.2 2.3 18.7 2 24.1 7.9 10.5 11.5 10.1 15.7 18 17.4 7.7 1.7 21.5 3.3 28.6-1 7.2-4.3 10-12.8 15.3-17 8.3-6.6 14-3.5 20.7-6.3 6.6-2.7 12.1-7.9 18.9-10 6.4-2 13.3-1.9 20-2.2 7-0.4 15.3 4 21.1 0 5.9-4 4.8-13.5 7.8-20 2.5-5.5 6.5-10.4 8.3-16.1 1.3-4.1 0.5-8.7 1.7-12.8 1.7-5.9 6.7-10.7 7.8-16.7 2-10.6-0.8-21.6-2.2-32.3-0.9-6.4-4.2-12.5-3.9-18.9 0.3-5.8 2.4-11.5 5-16.7 1.8-3.7 6.8-5.9 7.2-10 1.2-10.4-4-22.5-12.2-28.9-4.6-3.6-14.9 2.6-17.2-2.8-5.6-13 23.5-20.9 23.9-35 0.3-8.6-13.3-22.2-13.3-22.2l-5-60.1c0 0-1.6-8.9-1.1-13.3 0.6-5.8 6-11 5-16.7-0.9-4.8-8.8-6.8-8.9-11.7-0.1-6.1 10.2-8.9 10.6-15 0.5-7.8-7.4-13.7-11.1-20.6-4.1-7.6-9.4-14.7-12.2-22.8-1.4-3.9-3.2 0.5-2.2-12.2 0.1-9.5-14.8-18.5-10-26.7 2.5-4.2 11.7 1.8 14.5-2.2 14.2-21.2-16.9-49-17.8-74.5-0.2-4.5 4.3-9.3 2.2-13.3-1.9-3.7-8-2.8-11.1-5.6-3-2.7-5.5-6.2-6.7-10-1.5-5 0-10.4 0-15.6 0-4.4 1.2-9.1 0-13.3-1.4-4.6-5-8.3-7.8-12.2-1.1-1.5-3.1-2.6-3.3-4.4-0.6-4.8 3.9-8.8 5.6-13.3 1.2-3.3 3.9-6.5 3.3-10-0.9-5.5-4.3-12.8-9.8-13.3-1.9-0.2-2.3 4.2-4.2 4-8-1.1-10.4-12.9-12.6-20.7-3.6-26.5-14.5-23.1-15.4-35.2-1.3-25.9 11.6-67.9-9.4-89-8.7-8.8-27.1-11.6-36.9-4z">
<!-- Анимация цветов маски -->
<animate
id="Three_fire"
attributeName="fill"
dur="0.5s"
begin="svg1.click;Three_pause.end"
values=" #CD9AE9; white;white;#CD9AE9"
fill="freeze"
repeatCount="10" />
<!-- Пауза анимации цветов маски -->
<animate
id="Three_pause"
attributeName="fill"
dur="5s"
begin="Three_fire.end"
values=" white;white"
fill="freeze"
repeatCount="1" />
</path>
</mask>
</defs>
<image mask="url(#msk1)" xlink:href="https://i.stack.imgur.com/PBRad.jpg" height="100%" width="100%"/>
<path id="star_Full" fill="#CDE7FB" filter="url(#glow)" opacity="0" d="M580.2 76.2 837.2 71.3 774.7 5.7 845.1 62.7 850.1 8c0 0 6.8 54.2 6 54.7-4.3 2.8 70.9-57.5 70.9-57.5L862.8 71.2 1131.2 76.2 864.4 81.8 982.3 203C960.8 189.7 856.8 91.3 856.7 91.2L849.8 194.2 845.7 91.1 714.3 203.7 832.8 82.5Z">
<!-- Анимация opacity звезды -->
<animate
id="an_star"
attributeName="opacity"
dur="2.5s"
begin="svg1.click;pause_star.end"
values="1;1;0"
fill="freeze"
repeatCount="5" />
<!-- Пауза анимация opacity звезды -->
<animate
id="pause_star"
attributeName="opacity"
dur="1.5s"
begin="an_star.end"
values="0;0"
fill="freeze"
repeatCount="1" />
<!-- Замена цвета у звезды -->
<animate
id="fill_change_star"
attributeName="fill"
dur="11s"
begin="svg1.click;pause_star.end"
values="#CDE7FB;red;#B34EE9;#15C14E"
fill="freeze"
repeatCount="1" />
</path>
<!-- #CDE7FB;yellowgreen;crimson;#CDE7FB" -->
</svg>
Ламповая ASCII графика на Си. Посвящается всем, кто пишет на Си.
Осторожно. Возможен приступ эпилепсии (особенно если у Вас слабый ПК)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#define ROWS 19
#define COLS 26
void make_frame(const char ft[][COLS], const char sn[][COLS], char frame[][COLS]);
void rotate_snow(char sn[][COLS]);
void draw_frame(const char frame[][COLS]);
void delay(double sec);
void clear_screen();
int main()
{
const char fir_tree[ROWS][COLS] =
{
" ",
" ",
" ",
" /\\ ",
" < > ",
" /\\/\\ ",
" / o \\ ",
" /o \\ ",
" /_ _\\ ",
" / \\ ",
" / o \\ ",
" / o \\ ",
" /_ o _\\ ",
" / \\ ",
" / o \\ ",
" / o \\ ",
" /__o_____________\\ ",
" | | ",
"*************************"
};
char snow[ROWS - 1][COLS] =
{
" * * * * ",
" * * * * ",
" * * * *",
" * * * ",
" * * * ",
" * * ",
"* * * ",
" * * *",
" * * ",
" * * ",
"* * *",
" * ",
" * * * *",
" * ",
" * * ",
" * * * ",
" * * ",
" * * *",
};
char frame[ROWS][COLS];
while (1)
{
make_frame(fir_tree, snow, frame);
draw_frame(frame);
delay(0.1);
rotate_snow(snow);
clear_screen();
}
return 0;
}
void make_frame(const char ft[][COLS], const char sn[][COLS], char frame[][COLS])
{
strcpy(frame[ROWS - 1], ft[ROWS - 1]);
for (int i = 0; i < ROWS - 1; i++)
{
strcpy(frame[i], ft[i]);
for (int j = 0; j < COLS; j++)
{
if (sn[i][j] == '*')
{
frame[i][j] = '*';
}
}
}
}
void rotate_snow(char sn[][COLS])
{
char temp[COLS];
strcpy(temp, sn[ROWS - 2]);
for (int i = ROWS - 3; i >= 0; i--)
{
strcpy(sn[i + 1], sn[i]);
}
strcpy(sn[0], temp);
}
void draw_frame(const char frame[][COLS])
{
for (int i = 0; i < ROWS; i++)
{
puts(frame[i]);
}
}
void delay(double sec)
{
clock_t start = clock();
while ((double)(clock() - start) / CLOCKS_PER_SEC < sec)
continue;
}
void clear_screen()
{
system("cls");
}
елочка
window.onload = () => {
let C = document.querySelector('canvas'),
$ = C.getContext('2d'),
W = C.width = innerWidth,
H = C.height = innerHeight
function bg() {
$.fillStyle = '#000'
$.fillRect(0, 0, W, H)
}
function circle(x, y, r, color) {
$.fillStyle = color
$.beginPath()
$.arc(x, y, r, 0, Math.PI * 2)
$.fill()
}
function line(x1, y1, x2, y2, color) {
$.strokeStyle = color
$.beginPath()
$.moveTo(x1, y1)
$.lineTo(x2, y2)
$.stroke()
}
let count = 30,
x, y, z,
radius = H / 3,
r
let mouseY = .8 * H
let color,
alpha,
step = H / 50,
jmax = 10,
margin = .1 * H,
imax = H - margin,
angle
function loop() {
bg()
line(W / 2, margin, W / 2, H, 'rgba(0,255,0,.5)')
for (let j = 0; j < jmax; j++) {
for (let i = margin; i < imax; i += step) {
angle = (i / imax) * (count / (2 * Math.PI)) + (j / jmax) * 2 * Math.PI
x = W / 2 + ((i - 10) / imax) * radius * Math.cos(angle)
z = radius + ((i - 10) / imax) * radius * Math.sin(angle)
y = i + 100 * Math.sin((mouseY - H / 2) / (H / 2))
r = .0075 * z + .002 * y
alpha = (z / (2 * radius)) + .2
color = `rgba(255,255,255,${alpha/5})`
line(W / 2, i, x, y, color)
if (j % 2) {
color = `rgba(255,155,0,${alpha})`
} else {
color = `rgba(0,155,255,${alpha})`
}
circle(x, y, r, color)
}
}
count += Math.PI / 90
requestAnimationFrame(loop)
}
window.addEventListener('mousemove', e => mouseY = e.clientY)
loop()
window.addEventListener('resize', () => {
location.reload()
})
}
<body style="margin: 0; background: #000; overflow: hidden">
<canvas></canvas>
</body>
снежок
let snowmax = 40,
snowcolor = new Array("#aaaacc", "#ddddff", "#ccccdd", "#f3f3f3", "#f0ffff"),
snowtype = new Array("Arial Black", "Arial Narrow", "Times", "Comic Sans MS"),
snowletter = "*",
sinkspeed = 0.5,
snowmaxsize = 30,
snowminsize = 8,
snow = new Array(),
marginbottom,
marginright,
timer,
i_snow = 0,
x_mv = new Array(),
crds = new Array(),
lftrght = new Array();
function randommaker(range) {
rand = Math.floor(range * Math.random());
return rand;
}
function initsnow() {
marginbottom = window.innerHeight;
marginright = window.innerWidth;
let snowsizerange = snowmaxsize - snowminsize;
for (i = 0; i <= snowmax; i++) {
crds[i] = 0;
lftrght[i] = Math.random() * 15;
x_mv[i] = 0.03 + Math.random() / 10;
snow[i] = document.getElementById("s" + i);
snow[i].style.fontFamily = snowtype[randommaker(snowtype.length)];
snow[i].size = randommaker(snowsizerange) + snowminsize;
snow[i].fontSize = snow[i].size;
snow[i].style.color = snowcolor[randommaker(snowcolor.length)];
snow[i].sink = sinkspeed * snow[i].size / 5;
snow[i].posx = randommaker(marginright - snow[i].size);
snow[i].posy = randommaker(
2 * marginbottom - marginbottom - 2 * snow[i].size
);
snow[i].style.left = snow[i].posx;
snow[i].style.top = snow[i].posy;
}
movesnow();
}
function movesnow() {
for (i = 0; i <= snowmax; i++) {
crds[i] += x_mv[i];
snow[i].posy += snow[i].sink;
snow[i].style.left = snow[i].posx + lftrght[i] * Math.sin(crds[i]) + "px";
snow[i].style.top = snow[i].posy + "px";
if (
snow[i].posy >= marginbottom - 2 * snow[i].size ||
parseInt(snow[i].style.left) > marginright - 3 * lftrght[i]
) {
snow[i].posx = randommaker(marginright - snow[i].size);
snow[i].posy = 0;
}
}
let timer = setTimeout("movesnow()", 50);
}
for (i = 0; i <= snowmax; i++) {
document.write(
"<span id='s" +
i +
"'style='position: absolute; top: -" +
snowmaxsize +
"'>" +
snowletter +
"</span>"
);
}
initsnow();
window.addEventListener('resize', ()=> initsnow())
<body style="margin: 0; background: #222; overflow: hidden;"></body>
поздравлялка stroke-dash
body {
margin: 0;
background: #222;
font: 14em/1 cursive;
}
.line {
font-size: 0.6em;
}
svg {
position: absolute;
width: 100%;
height: 100%;
}
.text {
fill: none;
stroke-dasharray: 7% 27%;
stroke-width: 3px;
animation: stroke 9s infinite ease-in-out;
}
.text:nth-child(1) {
stroke: hotpink;
stroke-dashoffset: 7%;
}
.text:nth-child(2) {
stroke: firebrick;
stroke-dashoffset: 14%;
}
.text:nth-child(3) {
stroke: sandybrown;
stroke-dashoffset: 21%;
}
.text:nth-child(4) {
stroke: antiquewhite;
stroke-dashoffset: 28%;
}
.text:nth-child(5) {
stroke: steelblue;
stroke-dashoffset: 35%;
}
@keyframes stroke {
50% {
stroke-dashoffset: 36%;
stroke-dasharray: 0 88%;
}
}
<svg viewBox="0 0 800 600">
<symbol id="text">
<text text-anchor="middle" x="50%" y="40%" class="line">
С Новым
</text>
<text text-anchor="middle" x="50%" y="70%">
годом!
</text>
</symbol>
<g>
<use xlink:href="#text" class="text"></use>
<use xlink:href="#text" class="text"></use>
<use xlink:href="#text" class="text"></use>
<use xlink:href="#text" class="text"></use>
<use xlink:href="#text" class="text"></use>
</g>
</svg>
поздравлялка webgl
let vertexCount = 10000 * 4,
depth = 0,
fontName = "Arial, Helvetica, Verdana",
fontSize = 24,
frame = 0,
smoothness = 6;
let vertices = [],
dVertices = [];
let refctx = document.createElement("canvas").getContext("2d");
let gl = document.createElement("canvas").getContext("webgl");
let postctx = document.body
.appendChild(document.createElement("canvas"))
.getContext("2d");
let canvas = gl.canvas;
let compileShader = function(type, source) {
let shader = gl.createShader(type),
status;
gl.shaderSource(shader, source);
gl.compileShader(shader);
status = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (status) return shader;
console.error("Shader compile error", gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
};
let createProgram = function(vertexShader, fragmentShader) {
let program = gl.createProgram(),
status;
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
status = gl.getProgramParameter(program, gl.LINK_STATUS);
if (status) return program;
console.error("program link error", gl.getProgramInfoLog());
gl.deleteProgram(program);
};
let vertexShader = compileShader(
gl.VERTEX_SHADER,
`
attribute vec4 a_position;
uniform vec2 u_resolution;
uniform float u_frame;
varying vec4 v_position;
varying float v_frame;
void main () {
v_position = a_position;
v_frame = u_frame;
v_position.xy /= u_resolution;
v_position.y *= -1.0;
v_position.xy *= 10.0;
v_position.z += cos(u_frame / 20.0 + v_position.x * 10.0) * sin(u_frame / 10.0 + v_position.y * 12.0) * 0.02;
v_position.xy /= (1.0 + v_position.z);
gl_Position = vec4(v_position.xy, 0.0, 1.0);
gl_PointSize = 3.0;
}
`
);
let fragmentShader = compileShader(
gl.FRAGMENT_SHADER,
`
precision mediump float;
varying vec4 v_position;
varying float v_frame;
float pi = 3.141592653589793;
float hue2rgb(float f1, float f2, float hue) {
if (hue < 0.0)
hue += 1.0;
else if (hue > 1.0)
hue -= 1.0;
float res;
if ((6.0 * hue) < 1.0)
res = f1 + (f2 - f1) * 6.0 * hue;
else if ((2.0 * hue) < 1.0)
res = f2;
else if ((3.0 * hue) < 2.0)
res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
else
res = f1;
return res;
}
vec3 hsl2rgb(vec3 hsl) {
vec3 rgb;
hsl.x = mod(hsl.x, 360.0);
hsl.x /= 360.0;
if (hsl.y == 0.0) {
rgb = vec3(hsl.z); // Luminance
} else {
float f2;
if (hsl.z < 0.5)
f2 = hsl.z * (1.0 + hsl.y);
else
f2 = hsl.z + hsl.y - hsl.y * hsl.z;
float f1 = 2.0 * hsl.z - f2;
rgb.r = hue2rgb(f1, f2, hsl.x + (1.0/3.0));
rgb.g = hue2rgb(f1, f2, hsl.x);
rgb.b = hue2rgb(f1, f2, hsl.x - (1.0/3.0));
}
return rgb;
}
void main () {
vec4 col = vec4(hsl2rgb(vec3(v_frame + v_position.z * 2000.0, 1.0, .5)) * v_position.w, 1.0);
gl_FragColor = col;
}
`
);
let program = createProgram(vertexShader, fragmentShader);
let aPosition = gl.getAttribLocation(program, "a_position");
let uResolution = gl.getUniformLocation(program, "u_resolution");
let uColor = gl.getUniformLocation(program, "u_color");
let uFrame = gl.getUniformLocation(program, "u_frame");
let vertexBuffer = gl.createBuffer();
gl.useProgram(program);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAttribPointer(aPosition, 4, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(aPosition);
let render = () => {
frame++;
gl.uniform1f(uFrame, frame);
gl.clear(gl.COLOR_BUFFER_BIT);
if (
postctx.canvas.width !== postctx.canvas.offsetWidth ||
postctx.canvas.height !== postctx.canvas.offsetHeight
) {
canvas.width = postctx.canvas.width = postctx.canvas.offsetWidth;
canvas.height = postctx.canvas.height = postctx.canvas.offsetHeight;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.uniform2fv(uResolution, [canvas.width, canvas.height]);
}
for (let i = 0; i < vertices.length; i += 4) {
let x = i;
let y = i + 1;
let z = i + 2;
let v = i + 3;
dVertices[x] -= (dVertices[x] - vertices[x]) / smoothness;
dVertices[y] -= (dVertices[y] - vertices[y]) / smoothness;
dVertices[z] -= (dVertices[z] - vertices[z]) / smoothness;
dVertices[v] -= (dVertices[v] - vertices[v]) / smoothness * 2;
}
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(dVertices), gl.STATIC_DRAW);
gl.drawArrays(gl.POINTS, 0, dVertices.length / 4);
postctx.globalAlpha = 0.2;
postctx.globalCompositeOperation = "source-over";
postctx.drawImage(canvas, 0, 0);
postctx.globalCompositeOperation = "lighten";
postctx.globalAlpha = 1;
postctx.filter = "blur(8px)";
postctx.drawImage(canvas, 0, 0);
postctx.filter = "blur(0)";
requestAnimationFrame(render);
};
let setText = text => {
vertices = [];
refctx.font = fontSize.toString() + "px " + fontName;
refctx.canvas.width = refctx.measureText(text).width || 100;
refctx.canvas.height = fontSize;
refctx.font = fontSize.toString() + "px " + fontName;
refctx.textBaseline = "top";
refctx.clearRect(0, 0, refctx.canvas.width, refctx.canvas.height);
refctx.fillStyle = "#fff";
refctx.fillText(text, 0, 0);
let {
data
} = refctx.getImageData(
0,
0,
refctx.canvas.width,
refctx.canvas.height
);
for (let i = 0; i < vertexCount; i += 4) {
j = i % data.length;
let dI = (j / 4) >> 0;
let x = dI % refctx.canvas.width - refctx.canvas.width / 2;
let y =
((dI / refctx.canvas.width) >> 0) % refctx.canvas.height -
refctx.canvas.height / 2;
let z = -depth / 2 + Math.random() * depth;
let v = data[j] * (data[j + 3] / 255) / 255;
vertices.push(x);
vertices.push(y);
vertices.push(z);
vertices.push(v);
}
};
let textList = [
'С',
'новым',
'годом,',
'друзья!'
];
let textIndex = 0,
textGeneration = () => {
setText(textList[textIndex]);
setTimeout(() => {
textIndex++;
if (textIndex === textList.length) {
textIndex = 0;
}
textGeneration();
}, 1500);
};
textGeneration();
for (let i = 0; i < vertexCount; i++) {
dVertices.push(0);
}
render();
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
Всех с наступающим!
window.onload = function() {
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
particlesOnScreen = 245,
particlesArray = [],
w,
h;
if (window.innerWidth > 1600) {
particlesOnScreen = 350; /// for desktop
}
if (window.innerWidth < 680) {
particlesOnScreen = 140; /// for mobile
}
function random(min, max) {
return min + Math.random() * (max - min + 1);
}
function clientBrowseerViewbox() {
w = canvas.width = window.innerWidth;
h = canvas.height = window.innerHeight;
}
clientBrowseerViewbox();
function createSnowFlakes(particlesOnScreen) {
for (var i = 0; i < particlesOnScreen; i++) {
particlesArray.push({
x: Math.random() * w,
y: Math.random() * h,
opacity: Math.random(),
speedX: random(-1, 1),
speedy: random(3, 4),
radius: random(0.2, 3)
});
}
}
function drawSnowFlakes() {
for (var i = 0; i < particlesArray.length; i++) {
var gradient = ctx.createRadialGradient(
particlesArray[i].x,
particlesArray[i].y,
0,
particlesArray[i].x,
particlesArray[i].y,
particlesArray[i].radius
);
gradient.addColorStop(
0,
"rgba(255,255,255," + particlesArray[i].opacity + " )"
);
gradient.addColorStop(
0.7,
"rgba(210,236,242," + particlesArray[i].opacity + " )"
);
gradient.addColorStop(
1,
"rgba(237,247,249," + particlesArray[i].opacity + " )"
);
ctx.beginPath();
ctx.arc(
particlesArray[i].x,
particlesArray[i].y,
particlesArray[i].radius,
0,
Math.PI * 2,
false
);
ctx.fillStyle = gradient;
ctx.fill();
}
}
function moveSnowFlakes() {
for (var i = 0; i < particlesArray.length; i++) {
particlesArray[i].x += particlesArray[i].speedX;
particlesArray[i].y += particlesArray[i].speedy;
if (particlesArray[i].y > h) {
particlesArray[i].x = Math.random() * w * 1.5;
particlesArray[i].y = -50;
}
}
}
function updateSnowFall() {
ctx.clearRect(0, 0, w, h);
drawSnowFlakes();
moveSnowFlakes();
}
setInterval(updateSnowFall, 50);
function snowRun() {
createSnowFlakes(particlesOnScreen);
}
snowRun();
var addEvent = function(object, type, callback) {
if (object == null || typeof object == "undefined") return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
} else if (object.attachEvent) {
object.attachEvent("on" + type, callback);
} else {
object["on" + type] = callback;
}
};
addEvent(window, "resize", function(event) {
clientBrowseerViewbox();
snowRun();
});
};
canvas {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100vh;
z-index: 2;
}
body {
margin: 0;
padding: 0;
height: 100vh;
background-color: #19242c;
font-family: "Indie Flower", cursive;
color: #fff;
overflow: hidden;
}
h1 {
display: block;
position: absolute;
left: 50%;
text-align: center;
transform: translate(-50%, 1vh);
margin: 0;
min-width: 300px;
letter-spacing: 10px;
font-size: 6vw;
}
img {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100vw;
max-width: 450px;
}
@media only screen and (max-width: 600px) {
h1 {
font-size: 40px;
letter-spacing: 5px;
}
}
@media only screen and (max-width: 380px) {
h1 {
font-size: 36px;
letter-spacing: 3px;
}
img {
max-width: 250px;
}
}
@media only screen and (max-height: 460px) {
h1 {
font-size: 18px;
top: 5px;
}
img {
bottom: 0%;
width: 56vh;
}
}
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Indie+Flower&display=swap" rel="stylesheet">
<h1>Happy New Year</h1>
<img src="https://github.com/BlackStar1991/Pictures-for-sharing-/blob/master/NewYear/cathing-snowflakes.png?raw=true" alt="cathing-snowflakes">
<canvas id="canvas"></canvas>
<audio autoplay loop>
<source src="https://github.com/BlackStar1991/AudioSharing/blob/master/New%20Year/Train%20-%20Shake%20it%20up%2C%20it's%20Christmas%20time.mp3?raw=true" type="audio/mpeg"/>
<source src="https://github.com/BlackStar1991/AudioSharing/blob/master/New%20Year/Train%20-%20Shake%20it%20up%2C%20it's%20Christmas%20time.ogg?raw=true"/>
</audio>
PS Не ешьте желтый снег
Маленькой CSS елочке холодно в 2D
let cos = Math.cos, sin = Math.sin,
k = 1111, a1 = 0.5, a2 = -0.5,
far = 300, points = [];
let many = (n, f) => [...Array(n)].map((e,i) => f(i));
document.body.innerHTML += many(54, i => `<div class="plane" style="--h:${Math.floor(i/18)};background-color: rgb(0,${50+(i%18>9?18-i%18:i%18)*20},0);--r2:${i*360/18}deg;"></div>`).join('\n')
document.body.innerHTML += many(333, i => {
let x = Math.random()*100-50;
let y = Math.random()*200-170;
let z = Math.random()*100-50;
points.push({x, y, z});
return `<span style="color:white">*</span>`
}).join('\n')
let stars = document.querySelectorAll('span');
function clamp(v, min, max) {
return Math.min(Math.max(v, min), max);
}
function project(p, i) {
p.y += 1;
if (p.y > 30) p.y = -170;
let x = p.x*cos(a1) + p.z*sin(a1);
let z = p.z*cos(a1) - p.x*sin(a1);
let y = p.y*cos(a2) + z*sin(a2);
let d = z*cos(a2) - p.y*sin(a2) + far;
x = (k/d)*x + innerWidth/2;
y = (k/d)*y + innerHeight/2;
let cull = Math.abs(x) > innerWidth || Math.abs(y) > innerHeight
stars[i].style.transform = cull ? 'scale(0)': `translate(${x}px,${y}px)`;
}
requestAnimationFrame(render);
function render() {
requestAnimationFrame(render);
points.forEach(project);
}
addEventListener('mousemove', e => {
a1 = -e.x/5/180*Math.PI
a2 = (-20-e.y/20)/180*Math.PI
document.body.style.setProperty('--y', e.x/5 + 'deg')
document.body.style.setProperty('--x', -20-e.y/20 + 'deg')
})
body {
user-select: none;
background-color:black;
overflow: hidden;
perspective: 400px;
height: 100vh;
--r1: 10deg;
--x: -40deg;
--y: 100deg;
}
.plane {
clip-path: polygon(50% 0%,100% 100%, 0% 100%);
display: iniline-block;
--height: calc(100px - calc(var(--h) * 15.2px));
--width: calc(20px - calc(var(--h) * 3px));
width: var(--width);
height: var(--height);
top: calc(50% - calc(var(--height) * 0.5));
left: calc(50% - 10px);
position: absolute;
backface-visibility: hidden;
transform: rotateX(var(--x))
rotateY(calc(var(--r2) + calc(var(--y) + var(--r1))))
translateZ(calc(51.5px - calc(var(--h) * 8px)))
translateY(calc(var(--h) * -40px))
rotateX(31deg);
transform-origin: 50% 100%;
}
.snow {
position: absolute;
width:400px;
height:400px;
border-radius:22%;
background-color: #eeef;
--s: calc(50% - 200px);
top: var(--s);
left: var(--s);
transform: translateY(55px)
rotateX(calc(90deg + var(--x)))
rotateZ(calc(var(--y) * -1))
}
span {
position: fixed;
top: -13px;
left: -7px;
font-size: 20px;
}
<div class="snow"></div>
Сделал самую простую ёлочку ради интереса с помощью символов. Всех с наступающим!
let a = 19,
b = 6;
c = 10;
h = 1,
x = 0,
d = '<p style="color: green;" align="center">',
s = '⭐',
u = '✵',
p = '||||';
f = ['
Answer 13
фейерверк
window.onload = () => {
let C = document.querySelector('canvas'),
W = C.width = innerWidth,
H = C.height = innerHeight,
$ = C.getContext('2d')
window.addEventListener('resize', () => {
W = innerWidth
H = innerHeight
$.fillStyle = '#000'
$.fillRect(0, 0, W, H)
})
$.fillStyle = '#000'
$.fillRect(0, 0, W, H)
let listFire = [],
listFirework = [],
fireNumber = 10,
center = {
x: W / 2,
y: H / 2
},
range = 100,
i
for (i = 0; i < fireNumber; i++) {
let fire = {
x: Math.random() * range / 2 - range / 4 + center.x,
y: Math.random() * range * 2 + H,
size: Math.random() + 0.5,
fill: '#fd1',
vx: Math.random() - 0.5,
vy: -(Math.random() + 4),
ax: Math.random() * 0.02 - 0.01,
far: Math.random() * range + (center.y - range)
}
fire.base = {
x: fire.x,
y: fire.y,
vx: fire.vx
}
listFire.push(fire)
}
function randomColor() {
let r = Math.floor(Math.random() * 256),
g = Math.floor(Math.random() * 256),
b = Math.floor(Math.random() * 256)
color = `rgba(${r},${g},${b})`
return color
}
(function loop() {
requestAnimationFrame(loop)
update()
draw()
})();
function update() {
for (i = 0; i < listFire.length; i++) {
let fire = listFire[i]
if (fire.y <= fire.far) {
let color = randomColor()
for (i = 0; i < fireNumber; i++) {
let firework = {
x: fire.x,
y: fire.y,
size: Math.random() + 1.5,
fill: color,
vx: Math.random() * 5 - 2.5,
vy: Math.random() * -5 + 1.5,
ay: 0.05,
alpha: 1,
life: Math.round(Math.random() * range / 2) + range / 2
}
firework.base = {
life: firework.life,
size: firework.size
}
listFirework.push(firework)
}
fire.x = fire.base.x
fire.y = fire.base.y
fire.vx = fire.base.vx
fire.ax = Math.random() * 0.02 - 0.01
}
fire.x += fire.vx
fire.y += fire.vy
fire.vx += fire.ax
}
for (i = listFirework.length - 1; i >= 0; i--) {
let firework = listFirework[i]
if (firework) {
firework.x += firework.vx
firework.y += firework.vy
firework.vy += firework.ay
firework.alpha = firework.life / firework.base.life
firework.size = firework.alpha * firework.base.size
firework.alpha = firework.alpha > 0.6 ? 1 : firework.alpha
firework.life--
if (firework.life <= 0) {
listFirework.splice(i, 1)
}
}
}
}
function draw() {
$.globalCompositeOperation = 'source-over'
$.globalAlpha = 0.18
$.fillStyle = '#000'
$.fillRect(0, 0, W, H)
$.globalCompositeOperation = 'screen'
$.globalAlpha = 1
for (i = 0; i < listFire.length; i++) {
let fire = listFire[i]
$.beginPath()
$.arc(fire.x, fire.y, fire.size, 0, Math.PI * 2)
$.fillStyle = fire.fill
$.fill()
}
for (i = 0; i < listFirework.length; i++) {
let firework = listFirework[i]
$.globalAlpha = firework.alpha
$.beginPath()
$.arc(firework.x, firework.y, firework.size, 0, Math.PI * 2)
$.fillStyle = firework.fill
$.fill()
}
}
window.addEventListener('resize', () => location.reload())
}
<body style="margin: 0; overflow: hidden">
<canvas></canvas>
</body>
Answer 14
Вряд ли еще к этим 2м наработкам еще что-то прибавится, публиковал их не так давно в чатике, публикую тут:
РАЗ:
let chars, particles, canvas, ctx, w, h, current;
let duration = 5000;
let str = ['Happy', 'New', 'Year' , '2020'];
init();
resize();
requestAnimationFrame(render);
addEventListener('resize', resize);
function makeChar(c){
let tmp = document.createElement('canvas');
let size = tmp.width = tmp.height = w<400?200:300;
let tmpCtx = tmp.getContext('2d');
tmpCtx.font = 'bold '+size+'px Arial';
tmpCtx.fillStyle = 'white';
tmpCtx.textBaseline = "middle";
tmpCtx.textAlign = "center";
tmpCtx.fillText(c, size/2, size/2);
let char2 = tmpCtx.getImageData(0,0,size,size);
let char2particles = [];
for(var i=0; char2particles.length< particles; i++){
let x = size*Math.random();
let y = size*Math.random();
let offset = parseInt(y)*size*4 + parseInt(x)*4;
if(char2.data[offset])
char2particles.push([x-size/2,y-size/2])
}
return char2particles;
}
function init() {
canvas = document.createElement('canvas');
document.body.append(canvas);
document.body.style.margin = 0;
document.body.style.overflow = 'hidden'
document.body.style.background = 'black'
ctx = canvas.getContext('2d');
}
function resize() {
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
particles = innerWidth<400? 55 : 199;
}
function makeChars(t) {
let actual = parseInt(t / duration) % str.length;
if (current === actual)
return
current = actual;
chars = [...str[actual]].map(makeChar);
}
function render(t) {
makeChars(t);
requestAnimationFrame(render);
ctx.fillStyle = '#00000010'
ctx.fillRect(0, 0, w, h);
chars.forEach((pts,i) => firework(t, i, pts));
}
function firework(t, i, pts) {
t -= i*200;
let id = i + chars.length*parseInt(t - t%duration);
t = t % duration / duration;
let dx = (i+1)*w/(1+chars.length);
dx += Math.min(0.33, t)*100*Math.sin(id);
let dy = h*0.5;
dy += Math.sin(id*4547.411)*h*0.1;
if (t < 0.33) {
rocket(dx, dy, id, t*3);
} else {
explosion(pts, dx, dy, id, Math.min(1, Math.max(0, t-0.33)*2));
}
}
function rocket(x, y, id, t) {
ctx.fillStyle = 'white';
let r = 2-2*t + Math.pow(t, 15*t)*16;
y = h - y*t;
circle(x, y, r)
}
function explosion(pts, x, y, id, t) {
let dy = (t*t*t)*20;
let r = Math.sin(id)*1 + 3
r = t<0.5 ? (t+0.5)*t*r:r-t*r
ctx.fillStyle = `hsl(${id*55}, 55%, 55%)`;
pts.forEach((xy,i) => {
if (i%20 === 0)
ctx.fillStyle = `hsl(${id*55}, 55%, ${55+t*Math.sin(t*55+i)*45}%)`;
circle(t*xy[0] + x, h - y + t*xy[1] + dy, r)
});
}
function circle(x,y,r) {
ctx.beginPath();
ctx.ellipse(x, y, r, r, 0, 0, 6.283);
ctx.fill();
}
ДВА:
Update by @Alexandr_TT
Интересные эффекты спрятаны под капотом. Наведите мышку на фейерверк и подвигайте курсор.
document.body.innerHTML = `
<canvas id=canvas
style="background:black;position:fixed;top:0;left:0"></canvas>
<input oninput="changeColor(this,0)" type="color" value="#ff0000"
style="position:fixed;z-index:1">
<input oninput="changeColor(this,1)" type="color" value="#00ff00"
style="position:fixed;z-index:1;left:100px">
<input oninput="changeColor(this,2)" type="color" value="#0000ff"
style="position:fixed;z-index:1;left:200px">
`;
let ctx = canvas.getContext('2d');
let cos = Math.cos,
sin = Math.sin,
rnd = Math.random,
k = 500,
a1 = 0,
a2 = 0.3,
far = 300,
w,
h,
p,
data,
colors = [[255, 0, 0],[0,255,0],[0,0,255]],
points = [];
for (let i = 0; i < 1; i ++){
for (var _ = 0; _ < 1e4; _ ++) {
let x = rnd()*300-150;
let y = rnd()*300-150;
let z = rnd()*300-150;
let d = Math.sqrt(x*x + y*y + z*z);
if (d > 100 || d < 80)
continue;
let dt = rnd()/8;
let c = [255-(d-90)*25, (d-90)*25, 255-(d-90)*25];
let r = (d-90)/6;
points.push({x, y, z, c, dt, d, r});
}
}
for (let i = 0; i < 1; i ++){
for (var _ = 0; _ < 3e3; _ ++) {
let x = rnd()*300-150;
let y = rnd()*3-1;
let z = rnd()*300-150;
let d = Math.sqrt(x*x + y*y + z*z);
if (d > 150 || d < 140)
continue;
let dt = 0.0;
points.push({x, y, z, dt, d, r:2});
}
}
function particle(p, t, dy, dx, color) {
t = Math.max(0, t - p.dt);
let X = t*p.x + dx;
let Y = -dy + t*p.y;
let Z = t*p.z;
let x = X*cos(a1) + Z*sin(a1);
let z = Z*cos(a1) - X*sin(a1);
let y = Y*cos(a2) + z*sin(a2);
let d = z*cos(a2) - Y*sin(a2) + far;
x = (k/d)*x + w/2;
y = (k/d)*y + h/2;
let blink = 1;//sin(t*11);
let r = color[0]*blink;
let g = color[1]*blink;
let b = color[2]*blink;
circle(x, y, p.r, r, g, b);
}
function circle(x, y, R, r, g, b) {
for (let i = -R; i < R; i++) {
for (let j = -R; j < R; j++) {
let d = 0.1/Math.sqrt(i*i + j*j);
pixel(x+i, y+j, r*d, g*d, b*d);
}
}
}
function pixel (x, y, r, g, b) {
if (x<0 || y<0 || x>w || y>h)
return
let o = (parseInt(y)*w+parseInt(x))*4;
data.data[o] += r;
data.data[1+o] += g;
data.data[2+o] += b;
}
function firework(points, t, dx, color){
let dur = 5000;
t = t % dur / dur;
let dy;
if (t < 0.3) {
dy = -300 + 400*t*10/3
t = 0.01 + 0.005*sin(t*55);
} else {
t = (t-0.3)/7*10;
dy = 100 - t*40;
t = Math.pow(t, 0.1)
points.forEach(p => particle(p, t, dy, dx, color));
}
}
function render(t) {
requestAnimationFrame(render);
data = ctx.getImageData(0, 0, w, h);
firework(points, t, -200,colors[0]);
firework(points, t - 1000, 0,colors[1]);
firework(points, t - 2000, 200,colors[2]);
ctx.putImageData(data, 0, 0);
ctx.fillStyle = '#00000009';
ctx.fillRect(0,0,w,h)
}
addEventListener('wheel', e => k *= 1 - Math.sign(e.deltaY)*0.1);
addEventListener('mouseup', e => p = null);
addEventListener('mousedown', e => p = {x: e.x, y: e.y, a1, a2});
addEventListener('mousemove', e => {
if (!p) return
a1 = p.a1 - (e.x - p.x)/100;
a2 = p.a2 - (e.y - p.y)/100;
});
addEventListener('resize', e => {
w = canvas.width = innerWidth;
h = canvas.height = innerHeight;
});
dispatchEvent(new Event('resize'));
requestAnimationFrame(render);
function changeColor(el,i) {
colors[i] = [[1,3], [3,5], [5,7]]
.map(args => el.value.substring(...args))
.map(hex => parseInt(hex, 16));
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Суть программы в том, что пользователь вводит строку, которая посимвольно считывается и выводится на экранРеализована обработка ctrl+c, в результате...
Есть задание "Образовать строку из исходной, повторив 1-й и элемент 1 раз, 2-й элемент 2 раз, 3 и элемент – 3 раза и тд" на с++ с ассемблерными вставками
Пишу класс для работы с матрицамиУказатель и кол-во строк и столбцов сокрыто, это понятно