diff --git a/src/app/layout/passport/a.scss b/src/app/layout/passport/a.scss new file mode 100644 index 00000000..f17a3108 --- /dev/null +++ b/src/app/layout/passport/a.scss @@ -0,0 +1,126 @@ +body { + background : radial-gradient(ellipse at bottom, #1b2735 0%, #090a0f 100%); + height : 100vh; + overflow : hidden; + display : flex; + font-family : 'Anton', sans-serif; + justify-content: center; + align-items : center; +} + +$shooting-time: 3000ms; + +.night { + position : relative; + width : 100%; + height : 100%; + transform : rotateZ(45deg); + // animation: sky 200000ms linear infinite; +} + +.shooting_star { + position : absolute; + left : 50%; + top : 50%; + // width: 100px; + height : 2px; + background : linear-gradient(-45deg, rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); + border-radius: 999px; + filter : drop-shadow(0 0 6px rgba(105, 155, 255, 1)); + animation : + tail $shooting-time ease-in-out infinite, + shooting $shooting-time ease-in-out infinite; + + &::before { + content : ''; + position : absolute; + top : calc(50% - 1px); + right : 0; + // width: 30px; + height : 2px; + background : linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); + transform : translateX(50%) rotateZ(45deg); + border-radius: 100%; + animation : shining $shooting-time ease-in-out infinite; + } + + &::after { + // CodePen Error + // @extend .shooting_star::before; + + content : ''; + position : absolute; + top : calc(50% - 1px); + right : 0; + // width: 30px; + height : 2px; + background : linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); + transform : translateX(50%) rotateZ(45deg); + border-radius: 100%; + animation : shining $shooting-time ease-in-out infinite; + transform : translateX(50%) rotateZ(-45deg); + } + + @for $i from 1 through 20 { + &:nth-child(#{$i}) { + $delay : random(9999) + 0ms; + top : calc(50% - #{random(400) - 200px}); + left : calc(50% - #{random(300) + 0px}); + // animation-delay: $delay; + // opacity: random(50) / 100 + 0.5; + + &::before, + &::after { + // animation-delay: $delay; + } + } + } +} + +@keyframes tail { + 0% { + width: 0; + } + + 30% { + width: 100px; + } + + 100% { + width: 0; + } +} + +@keyframes shining { + 0% { + width: 0; + } + + 50% { + width: 30px; + } + + 100% { + width: 0; + } +} + +@keyframes shooting { + 0% { + transform: translateX(0); + } + + 100% { + transform: translateX(300px); + } +} + +@keyframes sky { + 0% { + transform: rotate(45deg); + } + + 100% { + transform: rotate(45 + 360deg); + } +} \ No newline at end of file diff --git a/src/app/layout/passport/d.scss b/src/app/layout/passport/d.scss index 1cc8e46f..f383b8e8 100644 --- a/src/app/layout/passport/d.scss +++ b/src/app/layout/passport/d.scss @@ -6,14 +6,14 @@ $shadow: (); @for $i from 1 through $n { $v: random(90) + 9; // left - $h: random(250) + 10; // top - $d: random(70)/10 + 9; // 秒數 + $h: random(150) + 10; // top + $d: random(70)/10 + 12; // 秒數 .meteor-#{$i} { position : absolute; top : $h + px; left : $v*1%; - width : 300px; + width : 130px; height : 1px; transform : rotate(-45deg); background-image: linear-gradient(to right, #fff, rgba(255, 255, 255, 0)); @@ -44,13 +44,21 @@ $shadow: (); opacity: 0; } + 13% { + margin-top : 600px; + margin-left : -900px; + opacity : 0.6; + animation-delay: 2000ms; + } + 15% { + opacity: 0; + } + + + 100% { margin-top : 700px; margin-left: -900px; opacity : 0; } - - 100% { - opacity: 0; - } } \ No newline at end of file diff --git a/src/app/layout/passport/particle.ts b/src/app/layout/passport/particle.ts index 719517b5..38c23fd6 100644 --- a/src/app/layout/passport/particle.ts +++ b/src/app/layout/passport/particle.ts @@ -2,26 +2,312 @@ export default class Particle { ParticleNetworkAnimation: any; PNA: any; - + getLimitedRandom = function (min: number, max: number, roundToInteger?: any) { + var number = Math.random() * (max - min) + min; + if (roundToInteger) { + number = Math.round(number); + } + return number; + }; + + returnRandomArrayitem = function (array: string | any[]) { + return array[Math.floor(Math.random() * array.length)]; + }; constructor() { this.ParticleNetworkAnimation = this.PNA = function () {}; } init() { - this.PNA.prototype.init = function (element: any) { - this.$el = document.getElementsByClassName(element); + this.PNA = { + init: function (element: any) { + this.$el = document.getElementsByClassName(element); - this.container = element; - this.canvas = document.createElement('canvas'); - this.sizeCanvas(); - this.container.appendChild(this.canvas); - this.ctx = this.canvas.getContext('2d'); - // this.particleNetwork = new ParticleNetwork(this); + this.container = element; + this.canvas = document.createElement('canvas'); + this.sizeCanvas(); + this.container.appendChild(this.canvas); + this.ctx = this.canvas.getContext('2d'); + // this.particleNetwork = new ParticleNetwork(this); + + this.bindUiActions(); + + return this; + }, + bindUiActions: function () { + (window as any).on('resize', () => { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + // this.sizeContainer(); + this.sizeCanvas(); + this.particleNetwork.createParticles(); + }); + }, + sizeCanvas: function () { + this.canvas.width = this.container.offsetWidth; + this.canvas.height = this.container.offsetHeight; + } + }; + + var Particle = (parent: any, x: any, y: any) => { + const network = parent; + const canvas = parent.canvas; + return { + ctx: parent.ctx, + particleColor: this.returnRandomArrayitem(network.options.particleColors), + radius: this.getLimitedRandom(1.5, 2.5), + opacity: 0, + x: x || Math.random() * canvas.width, + y: y || Math.random() * canvas.height, + velocity: { + x: (Math.random() - 0.5) * parent.options.velocity, + y: (Math.random() - 0.5) * parent.options.velocity + } + }; + }; + + Particle.prototype.update = function () { + if (this.opacity < 1) { + this.opacity += 0.01; + } else { + this.opacity = 1; + } + // Change dir if outside map + if (this.x > this.canvas.width + 100 || this.x < -100) { + this.velocity.x = -this.velocity.x; + } + if (this.y > this.canvas.height + 100 || this.y < -100) { + this.velocity.y = -this.velocity.y; + } + + // Update position + this.x += this.velocity.x; + this.y += this.velocity.y; + }; + + Particle.prototype.draw = function () { + // Draw particle + this.ctx.beginPath(); + this.ctx.fillStyle = this.particleColor; + this.ctx.globalAlpha = this.opacity; + this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI); + this.ctx.fill(); + }; + + var ParticleNetwork = (parent: { canvas: any; ctx: any }) => { + this.init(); + return { + options: { + velocity: 1, // the higher the faster + density: 15000, // the lower the denser + netLineDistance: 200, + netLineColor: '#929292', + particleColors: ['#aaa'] // ['#6D4E5C', '#aaa', '#FFC458' ] + }, + canvas: parent.canvas, + ctx: parent.ctx + }; + }; + + ParticleNetwork.prototype.init = function () { + // Create particle objects + this.createParticles(true); + + // Update canvas + this.animationFrame = requestAnimationFrame(this.update.bind(this)); this.bindUiActions(); + }; - return this; + ParticleNetwork.prototype.createParticles = function (isInitial: any) { + // Initialise / reset particles + var me = this; + this.particles = []; + var quantity = (this.canvas.width * this.canvas.height) / this.options.density; + + if (isInitial) { + var counter = 0; + clearInterval(this.createIntervalId); + this.createIntervalId = setInterval(() => { + if (counter < quantity - 1) { + // Create particle object + this.particles.push(new Particle(me, me.canvas.width, me.canvas.height)); + } else { + clearInterval(me.createIntervalId); + } + counter++; + }, 50); + } else { + // Create particle objects + for (var i = 0; i < quantity; i++) { + this.particles.push(new Particle(this)); + } + } + }; + + ParticleNetwork.prototype.createInteractionParticle = function () { + // Add interaction particle + this.interactionParticle = new Particle(this); + this.interactionParticle.velocity = { + x: 0, + y: 0 + }; + this.particles.push(this.interactionParticle); + return this.interactionParticle; + }; + + ParticleNetwork.prototype.removeInteractionParticle = function () { + // Find it + var index = this.particles.indexOf(this.interactionParticle); + if (index > -1) { + // Remove it + this.interactionParticle = undefined; + this.particles.splice(index, 1); + } + }; + + ParticleNetwork.prototype.update = function () { + if (this.canvas) { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + this.ctx.globalAlpha = 1; + + // Draw connections + for (var i = 0; i < this.particles.length; i++) { + for (var j = this.particles.length - 1; j > i; j--) { + var distance, + p1 = this.particles[i], + p2 = this.particles[j]; + + // check very simply if the two points are even a candidate for further measurements + distance = Math.min(Math.abs(p1.x - p2.x), Math.abs(p1.y - p2.y)); + if (distance > this.options.netLineDistance) { + continue; + } + + // the two points seem close enough, now let's measure precisely + distance = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)); + if (distance > this.options.netLineDistance) { + continue; + } + + this.ctx.beginPath(); + this.ctx.strokeStyle = this.options.netLineColor; + this.ctx.globalAlpha = ((this.options.netLineDistance - distance) / this.options.netLineDistance) * p1.opacity * p2.opacity; + this.ctx.lineWidth = 0.7; + this.ctx.moveTo(p1.x, p1.y); + this.ctx.lineTo(p2.x, p2.y); + this.ctx.stroke(); + } + } + + // Draw particles + for (var i = 0; i < this.particles.length; i++) { + this.particles[i].update(); + this.particles[i].draw(); + } + + if (this.options.velocity !== 0) { + this.animationFrame = requestAnimationFrame(this.update.bind(this)); + } + } else { + cancelAnimationFrame(this.animationFrame); + } + }; + + ParticleNetwork.prototype.bindUiActions = function () { + // Mouse / touch event handling + this.spawnQuantity = 3; + this.mouseIsDown = false; + this.touchIsMoving = false; + + this.onMouseMove = (e: { offsetX: any; offsetY: any }) => { + if (!this.interactionParticle) { + this.createInteractionParticle(); + } + this.interactionParticle.x = e.offsetX; + this.interactionParticle.y = e.offsetY; + }; + + this.onTouchMove = (e: { preventDefault: () => void; changedTouches: { clientX: any[]; clientY: any }[] }) => { + e.preventDefault(); + this.touchIsMoving = true; + if (!this.interactionParticle) { + this.createInteractionParticle(); + } + this.interactionParticle.x = e.changedTouches[0].clientX; + this.interactionParticle.y = e.changedTouches[0].clientY; + }; + + this.onMouseDown = () => { + this.mouseIsDown = true; + var counter = 0; + var quantity = this.spawnQuantity; + var intervalId = setInterval( + () => { + if (this.mouseIsDown) { + if (counter === 1) { + quantity = 1; + } + for (var i = 0; i < quantity; i++) { + if (this.interactionParticle) { + this.particles.push(new Particle(this, this.interactionParticle.x, this.interactionParticle.y)); + } + } + } else { + clearInterval(intervalId); + } + counter++; + }, + 50 + ); + }; + + this.onTouchStart = (e: { preventDefault: () => void; changedTouches: { clientY: any; }[]; }) => { + e.preventDefault(); + setTimeout( + () => { + if (!this.touchIsMoving) { + for (var i = 0; i < this.spawnQuantity; i++) { + this.particles.push(new Particle(this, e.changedTouches[0].clientX, e.changedTouches[0].clientY)); + } + } + }, + 200 + ); + }; + + this.onMouseUp = () => { + this.mouseIsDown = false; + }; + + this.onMouseOut = function () { + this.removeInteractionParticle(); + }; + + this.onTouchEnd = (e: { preventDefault: () => void; }) => { + e.preventDefault(); + this.touchIsMoving = false; + this.removeInteractionParticle(); + }; + + // this.canvas.addEventListener('mousemove', this.onMouseMove); + // this.canvas.addEventListener('touchmove', this.onTouchMove); + // this.canvas.addEventListener('mousedown', this.onMouseDown); + // this.canvas.addEventListener('touchstart', this.onTouchStart); + // this.canvas.addEventListener('mouseup', this.onMouseUp); + // this.canvas.addEventListener('mouseout', this.onMouseOut); + // this.canvas.addEventListener('touchend', this.onTouchEnd); + }; + + ParticleNetwork.prototype.unbindUiActions = function () { + if (this.canvas) { + // this.canvas.removeEventListener('mousemove', this.onMouseMove); + // this.canvas.removeEventListener('touchmove', this.onTouchMove); + // this.canvas.removeEventListener('mousedown', this.onMouseDown); + // this.canvas.removeEventListener('touchstart', this.onTouchStart); + // this.canvas.removeEventListener('mouseup', this.onMouseUp); + // this.canvas.removeEventListener('mouseout', this.onMouseOut); + // this.canvas.removeEventListener('touchend', this.onTouchEnd); + } }; } } diff --git a/src/app/layout/passport/passport.component.html b/src/app/layout/passport/passport.component.html index 49068b17..bd54d788 100644 --- a/src/app/layout/passport/passport.component.html +++ b/src/app/layout/passport/passport.component.html @@ -9,14 +9,14 @@ --> - +
- +
@@ -40,5 +40,27 @@
+
\ No newline at end of file