diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2021-12-29 04:13:45 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2021-12-29 04:13:45 +0900 |
| commit | 5c7181ca214d268c8958b15caf295339942d6c93 (patch) | |
| tree | 8f9cfee50ac49e568bf78f1439abb9f96705d1b9 | |
| parent | Update .eslintrc.js (diff) | |
| download | misskey-5c7181ca214d268c8958b15caf295339942d6c93.tar.gz misskey-5c7181ca214d268c8958b15caf295339942d6c93.tar.bz2 misskey-5c7181ca214d268c8958b15caf295339942d6c93.zip | |
feat(client): svg sparkle effect
Resolve #8088
| -rw-r--r-- | CHANGELOG.md | 1 | ||||
| -rw-r--r-- | packages/client/assets/sparkle-spritesheet.png | bin | 185 -> 0 bytes | |||
| -rw-r--r-- | packages/client/src/components/sparkle.vue | 236 |
3 files changed, 106 insertions, 131 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index d5fa7adfd5..b86365f43e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Improvements - クライアント: ノートプレビューの精度を改善 +- クライアント: MFM sparkleエフェクトの改善 ### Bugfixes - クライアント: 一部のコンポーネントが裏に隠れるのを修正 diff --git a/packages/client/assets/sparkle-spritesheet.png b/packages/client/assets/sparkle-spritesheet.png Binary files differdeleted file mode 100644 index 0defccaf07..0000000000 --- a/packages/client/assets/sparkle-spritesheet.png +++ /dev/null diff --git a/packages/client/src/components/sparkle.vue b/packages/client/src/components/sparkle.vue index 21b57f1580..a365a15edf 100644 --- a/packages/client/src/components/sparkle.vue +++ b/packages/client/src/components/sparkle.vue @@ -1,14 +1,69 @@ <template> <span class="mk-sparkle"> - <span ref="content"> + <span ref="el"> <slot></slot> </span> - <canvas ref="canvas"></canvas> + <!-- なぜか path に対する key が機能しないため + <svg :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg"> + <path v-for="particle in particles" :key="particle.id" style="transform-origin: center; transform-box: fill-box;" + :transform="`translate(${particle.x} ${particle.y})`" + :fill="particle.color" + d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z" + > + <animateTransform + attributeName="transform" + attributeType="XML" + type="rotate" + from="0 0 0" + to="360 0 0" + :dur="`${particle.dur}ms`" + repeatCount="indefinite" + additive="sum" + /> + <animateTransform + attributeName="transform" + attributeType="XML" + type="scale" + :values="`0; ${particle.size}; 0`" + :dur="`${particle.dur}ms`" + repeatCount="indefinite" + additive="sum" + /> + </path> + </svg> + --> + <svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg"> + <path style="transform-origin: center; transform-box: fill-box;" + :transform="`translate(${particle.x} ${particle.y})`" + :fill="particle.color" + d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z" + > + <animateTransform + attributeName="transform" + attributeType="XML" + type="rotate" + from="0 0 0" + to="360 0 0" + :dur="`${particle.dur}ms`" + repeatCount="1" + additive="sum" + /> + <animateTransform + attributeName="transform" + attributeType="XML" + type="scale" + :values="`0; ${particle.size}; 0`" + :dur="`${particle.dur}ms`" + repeatCount="1" + additive="sum" + /> + </path> + </svg> </span> </template> <script lang="ts"> -import { defineComponent } from 'vue'; +import { defineComponent, onMounted, onUnmounted, ref } from 'vue'; import * as os from '@/os'; const sprite = new Image(); @@ -25,137 +80,56 @@ export default defineComponent({ required: true, }, }, - data() { - return { - sprites: [0,6,13,20], - particles: [], - anim: null, - ctx: null, - }; - }, - unmounted() { - window.cancelAnimationFrame(this.anim); - }, - mounted() { - this.ctx = this.$refs.canvas.getContext('2d'); - - new ResizeObserver(this.resize).observe(this.$refs.content); - - this.resize(); - this.tick(); - }, - updated() { - this.resize(); - }, - methods: { - createSparkles(w, h, count) { - const holder = []; - - for (let i = 0; i < count; i++) { - - const color = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6); - - holder[i] = { - position: { - x: Math.floor(Math.random() * w), - y: Math.floor(Math.random() * h) - }, - style: this.sprites[ Math.floor(Math.random() * 4) ], - delta: { - x: Math.floor(Math.random() * 1000) - 500, - y: Math.floor(Math.random() * 1000) - 500 - }, - color: color, - opacity: Math.random(), - }; - - } - - return holder; - }, - draw(time) { - this.ctx.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height); - this.ctx.beginPath(); - - const particleSize = Math.floor(this.fontSize / 2); - this.particles.forEach((particle) => { - var modulus = Math.floor(Math.random()*7); - if (Math.floor(time) % modulus === 0) { - particle.style = this.sprites[ Math.floor(Math.random()*4) ]; - } + setup(props) { + const particles = ref([]); + const el = ref<HTMLElement>(); + const width = ref(0); + const height = ref(0); + const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202']; - this.ctx.save(); - this.ctx.globalAlpha = particle.opacity; - this.ctx.drawImage(sprite, particle.style, 0, 7, 7, particle.position.x, particle.position.y, particleSize, particleSize); - - this.ctx.globalCompositeOperation = "source-atop"; - this.ctx.globalAlpha = 0.5; - this.ctx.fillStyle = particle.color; - this.ctx.fillRect(particle.position.x, particle.position.y, particleSize, particleSize); - - this.ctx.restore(); + onMounted(() => { + const ro = new ResizeObserver((entries, observer) => { + width.value = el.value?.offsetWidth + 64; + height.value = el.value?.offsetHeight + 64; }); - this.ctx.stroke(); - }, - tick() { - this.anim = window.requestAnimationFrame((time) => { - if (!this.$refs.canvas) { - return; - } - this.particles.forEach((particle) => { - if (!particle) { - return; - } - var randX = Math.random() > Math.random() * 2; - var randY = Math.random() > Math.random() * 3; - - if (randX) { - particle.position.x += (particle.delta.x * this.speed) / 1500; - } - - if (!randY) { - particle.position.y -= (particle.delta.y * this.speed) / 800; - } - - if( particle.position.x > this.$refs.canvas.width ) { - particle.position.x = -7; - } else if (particle.position.x < -7) { - particle.position.x = this.$refs.canvas.width; - } - - if (particle.position.y > this.$refs.canvas.height) { - particle.position.y = -7; - particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width); - } else if (particle.position.y < -7) { - particle.position.y = this.$refs.canvas.height; - particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width); - } - - particle.opacity -= 0.005; - - if (particle.opacity <= 0) { - particle.opacity = 1; - } - }); - - this.draw(time); + ro.observe(el.value); + let stop = false; + const add = () => { + if (stop) return; + const x = (Math.random() * (width.value - 64)); + const y = (Math.random() * (height.value - 64)); + const sizeFactor = Math.random(); + const particle = { + id: Math.random().toString(), + x, + y, + size: 0.2 + ((sizeFactor / 10) * 3), + dur: 1000 + (sizeFactor * 1000), + color: colors[Math.floor(Math.random() * colors.length)], + }; + particles.value.push(particle); + window.setTimeout(() => { + particles.value = particles.value.filter(x => x.id !== particle.id); + }, particle.dur - 100); - this.tick(); + window.setTimeout(() => { + add(); + }, 500 + (Math.random() * 500)); + }; + add(); + onUnmounted(() => { + ro.disconnect(); + stop = true; }); - }, - resize() { - if (this.$refs.content) { - const contentRect = this.$refs.content.getBoundingClientRect(); - this.fontSize = parseFloat(getComputedStyle(this.$refs.content).fontSize); - const padding = this.fontSize * 0.2; + }); - this.$refs.canvas.width = parseInt(contentRect.width + padding); - this.$refs.canvas.height = parseInt(contentRect.height + padding); - - this.particles = this.createSparkles(this.$refs.canvas.width, this.$refs.canvas.height, this.count); - } - }, + return { + el, + width, + height, + particles, + }; }, }); </script> @@ -169,10 +143,10 @@ export default defineComponent({ display: inline-block; } - > canvas { + > svg { position: absolute; - top: -0.1em; - left: -0.1em; + top: -32px; + left: -32px; pointer-events: none; } } |