Космический загрузчик
Прелоадер в виде разгоняющейся по орбите космической ракете, которая в конце анимации улетает в открытый космос
HTML
<svg viewBox="0 0 600 600" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="rocketPath" d="M300,80c121.5,0,220,98.5,220,220S421.5,520,300,520,80,421.5,80,300,178.5,80,300,80" fill="none" stroke="#e7dece" stroke-miterlimit="10"/>
<filter id="glow" x="-100%" y="-100%" width="250%" height="250%">
<feGaussianBlur stdDeviation="15" result="coloredBlur" />
<feOffset dx="0" dy="0" result="offsetblur"></feOffset>
<feFlood id="glowAlpha" flood-color="#41C2F4" flood-opacity="0.71"></feFlood>
<feComposite in2="offsetblur" operator="in"></feComposite>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<circle class="dot" cx="0" cy="0" r="4" fill="red"/>
<radialGradient id="dotGrad" cx="0" cy="0" r="33" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:1"/>
<stop offset="0.3" style="stop-color:#0867C7;stop-opacity:0.6"/>
<stop offset="0.8" style="stop-color:#081029;stop-opacity:0"/>
</radialGradient>
</defs>
<g class="container" />
<g>
<circle class="ringBg" cx="0" cy="0" r="4" stroke="#64A8AF" stroke-width="60" fill="none" opacity="0.085"/>
<circle class="ring" filter="url(#glow)" cx="0" cy="0" r="4" stroke="#38DFF1" stroke-width="13" stroke-linecap="round" fill="#26247D" fill-opacity="0.15" opacity=".31"/>
</g>
<g id="rocket">
<g>
<path d="M20.91,13.4v9.83H63.08V16q-9.82-5.84-22.88-5.64A75.41,75.41,0,0,0,20.91,13.4" fill="#e7dece"/>
<path d="M63.08,30.49V23.23H20.91v9.84A75.81,75.81,0,0,0,40.2,36.13Q53.25,36.32,63.08,30.49Z" fill="#fff7f0"/>
<circle cx="52.41" cy="23.17" r="4.15" fill="#a5c6e7"/>
</g>
<path d="M40.2,36.13a75.81,75.81,0,0,1-19.29-3.06q-5.22,9.41-5.84,13.39Q27,46.1,40.2,36.13" fill="#2a47c4"/>
<path d="M20.91,13.4A75.41,75.41,0,0,1,40.2,10.33Q27,.37,15.07,0,15.7,4,20.91,13.4Z" fill="#2a47c4"/>
<path d="M15.57,19.29v9l5.34,1.55V16.48l-5.34,1.66Z" fill="#51576b"/>
<path d="M71.43,23.92a1.82,1.82,0,0,0,.13-.68,2.09,2.09,0,0,0-.11-.63,3.14,3.14,0,0,0-.56-1q-2.33-3-7.81-5.6V30.49q5.48-2.58,7.81-5.59a3.7,3.7,0,0,0,.54-1" fill="#2a47c4"/>
<g id="rocketFlame">
<path d="M15.57,21.8c-3.68-.21-5.93.26-6.77,1.42.84,1.15,3.09,1.62,6.77,1.41Z" fill="#edbd52"/>
<path d="M15.57,21.8V19.29Q7.14,16.35,0,23.14,7.14,30,15.57,27.08V24.63c-3.68.21-5.93-.26-6.77-1.41C9.64,22.06,11.89,21.59,15.57,21.8Z" fill="#d64231"/>
</g>
<path d="M42.85,23.17c-9.58-3.18-28.49-1-28.32.06C14.48,24.4,33.29,26.85,42.85,23.17Z" fill="#2a47c4"/>
</g>
<!-- <circle id="dot" fill="url(#dotGrad)" cx="300" cy="120" r="33"/> -->
</svg>
CSS
body {
background-color: #000;
overflow: hidden;
text-align:center;
display: flex;
align-items: center;
justify-content: center;
}
body,
html {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
svg {
width: 100%;
height: 100%;
visibility: hidden;
}
TS
class App {
select = e => document.querySelector(e);
selectAll = e => document.querySelectorAll(e);
mainTl = new TimelineMax({repeat:-1, delay:1});
constructor(){
const dotArray = [];
const container = this.select('.container');
const ring = this.select('.ring');
const num = 160;
const stageWidth = 600;
const stageHeight = 600;
const mainCircleRadius = 220;
const strokeWidth = 120;
const duration = 8;
const colorArray = ["#25245F", '#26247D', '#303894', '#1F45B9', '#2F5AA0', '#3276CD', '#3B94E6', '#41C2F4', '#50F7FE', '#7DFFFE', '#EABEFC', '#64A8AF'];
const tl = new TimelineMax({});
const introTl = new TimelineMax({});
const step = 360 / num;
const rocket = this.select('#rocket');
const rocketPath = this.select('#rocketPath');
const bezier = MorphSVGPlugin.pathDataToBezier(rocketPath, {
offsetX: -71.4/2,
offsetY: -46/2
})
TweenMax.to('#rocketFlame', 0.03, {
scaleX:0.59,
repeat:-1,
yoyo:true,
transformOrigin:'100% 50%',
ease:Linear.easeNone
})
const rocketTl = new TimelineMax({paused:true});
rocketTl.to(rocket, 2, {
bezier: {
type: "cubic",
values: bezier,
autoRotate: true
},
ease: Linear.easeNone
})
TweenMax.set([ring, '.ringBg'], {
attr:{
r:mainCircleRadius,
cx:stageWidth/2,
cy:stageHeight/2
},
rotation:-90,
svgOrigin:`${stageWidth/2} ${stageHeight/2}`
})
TweenMax.set('.ringBg', {
strokeWidth:strokeWidth
})
TweenMax.set(rocket, {
transformOrigin:'50% 50%'
})
for(let i = 0; i < num; i++){
let clone = this.select('.dot').cloneNode(true);
container.appendChild(clone);
let angle = step * i;
let point = {
x: (Math.cos(angle * Math.PI / 180) * mainCircleRadius) + stageWidth/2,
y: (Math.sin(angle * Math.PI / 180) * mainCircleRadius) + stageHeight/2
}
TweenMax.set(clone, {
x:this.randomBetween(point.x+(strokeWidth/4), point.x - (strokeWidth/4)),
y:this.randomBetween(point.y+(strokeWidth/4), point.y - (strokeWidth/4)),
attr:{
r:this.randomBetween(1, 7)/10
},
fill:colorArray[i% colorArray.length],
alpha:0,
transformOrigin:'50% 50%'
});
dotArray.push(clone);
}
introTl.fromTo(rocket, 2, {
x:bezier[0].x-300,
y:bezier[0].y,
alpha:0
},{
x:bezier[0].x,
y:bezier[0].y,
alpha:1,
ease:Elastic.easeOut.config(0.3, 0.6)
}).staggerFrom(['.ringBg', ring], 3, {
scale:0,
strokeWidth:0,
ease:Elastic.easeOut.config(0.49, 0.6)
}, 0.21, 0)
.staggerTo(dotArray, 1, {
alpha:1,
delay:0.5,
ease:Linear.easeNone
}, 0.0021, 0)
var rotatingStarsTl = new TimelineMax({paused:false})
rotatingStarsTl.staggerTo(dotArray, 10, {
cycle:{
duration: () => {
return this.randomBetween(duration+3, 22)
}
},
rotation:-360,
svgOrigin:`${stageWidth/2} ${stageHeight/2}`,
//repeat: -1,
ease:Linear.easeNone
},0);
tl.addCallback(() => {rotatingStarsTl.play();})
.from(ring, duration, {
drawSVG:'0% 0%',
//rotation:720*-1.5,
ease:Linear.easeNone
})
.to(rocketTl, duration, {
time:rocketTl.duration(),
ease:Linear.easeNone
},0)
.staggerFromTo(dotArray, 1, {
alpha:1
},{
cycle:{
y:(i) => {
return '-=' + this.randomBetween(45, 85)
},
duration: () => {
return this.randomBetween(20, 40)/10
}
},
alpha:0,
immediateRender:false,
ease:Sine.easeOut
}, 0.0009, duration )
.addCallback(() => {rotatingStarsTl.pause();}, duration)
.to(['.ringBg', ring] , 1, {
alpha:0,
stroke:'#FFF',
strokeWidth:0,
ease:Expo.easeOut
},duration)
.staggerTo(dotArray, 0.2, {
//fill:'#FFF',
scale:'+=1',
//immediateRender:false,
transformOrigin:'50% 50%'
}, 0.0009, duration)
.to(rocket, 2, {
x:bezier[0].x+200,
alpha:0,
scale:0
}, duration)
this.mainTl.add(introTl)
.add(tl, 2)
.add(rotatingStarsTl, 2);
//ScrubGSAPTimeline(this.mainTl)
}
randomBetween (min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
onUpdate(){
console.log(this)
}
get timeline():boolean {
return this.mainTl;
}
}
TweenMax.set('svg', {
visibility: 'visible'
})
var app = new App();
//TweenMax.globalTimeScale(0.5)