animation
@ -1,8 +1,8 @@
|
|||||||
import Component from "@glimmer/component";
|
import Component from "@glimmer/component";
|
||||||
|
|
||||||
export default class WordCloud extends Component {
|
export default class WordCloud extends Component {
|
||||||
get randomModifier() {
|
get randomStyle() {
|
||||||
return Math.random();
|
return `--rand: ${Math.random()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -10,13 +10,16 @@ export default class WordCloud extends Component {
|
|||||||
<h3 class="rewind-report-title">Most used words</h3>
|
<h3 class="rewind-report-title">Most used words</h3>
|
||||||
<div class="rewind-report-container">
|
<div class="rewind-report-container">
|
||||||
{{#each-in @report.data as |word count|}}
|
{{#each-in @report.data as |word count|}}
|
||||||
<div
|
<div class="rewind-card__wrapper" style={{this.randomStyle}}>
|
||||||
class="rewind-card__wrapper"
|
<div class="rewind-card__inner">
|
||||||
style="--rand: {{this.randomModifier}}"
|
<div class="rewind-card -front">
|
||||||
>
|
<span class="rewind-card__title">?</span>
|
||||||
<div class="rewind-card">
|
<span class="rewind-card__data">{{count}}x</span>
|
||||||
<span class="rewind-card__title">{{word}}</span>
|
</div>
|
||||||
<span class="rewind-card__data">{{count}} times</span>
|
<div class="rewind-card -back">
|
||||||
|
<span class="rewind-card__title">{{word}}</span>
|
||||||
|
<span class="rewind-card__data">used {{count}} times</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each-in}}
|
{{/each-in}}
|
||||||
|
@ -28,6 +28,14 @@ export default class Rewind extends Component {
|
|||||||
|
|
||||||
@tracked fullScreen = true;
|
@tracked fullScreen = true;
|
||||||
@tracked loadingRewind = false;
|
@tracked loadingRewind = false;
|
||||||
|
@tracked shouldAnimate = false; // Controls animation state
|
||||||
|
@tracked imagesLoaded = false;
|
||||||
|
imageCache = [];
|
||||||
|
imageIndex = 1;
|
||||||
|
frameCount = 11;
|
||||||
|
animationFrameId = null;
|
||||||
|
lastScrollPosition = 0;
|
||||||
|
scrollThreshold = 6; // How many pixels to scroll before frame change
|
||||||
|
|
||||||
@action
|
@action
|
||||||
registerScrollWrapper(element) {
|
registerScrollWrapper(element) {
|
||||||
@ -46,6 +54,51 @@ export default class Rewind extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
preLoadImages() {
|
||||||
|
const preloadPromises = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.frameCount; i++) {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = `/plugins/discourse-rewind/images/bg-frames/bg-2_${i + 1}.png`;
|
||||||
|
this.imageCache[i] = img;
|
||||||
|
|
||||||
|
preloadPromises.push(
|
||||||
|
new Promise((resolve) => {
|
||||||
|
img.onload = resolve;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all images to load
|
||||||
|
Promise.all(preloadPromises).then(() => {
|
||||||
|
this.imagesLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
updateBackground() {
|
||||||
|
// Only continue if we should be animating
|
||||||
|
if (this.shouldAnimate) {
|
||||||
|
const children =
|
||||||
|
this.rewindContainer.getElementsByClassName("background-2");
|
||||||
|
|
||||||
|
if (children.length > 0 && this.imageCache[this.imageIndex]) {
|
||||||
|
const imageUrl = this.imageCache[this.imageIndex].src;
|
||||||
|
children[0].style.background = `url(${imageUrl})`;
|
||||||
|
children[0].style.backgroundSize = "contain";
|
||||||
|
|
||||||
|
// Update image index
|
||||||
|
this.imageIndex = (this.imageIndex + 1) % this.frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule the next frame
|
||||||
|
this.animationFrameId = requestAnimationFrame(() =>
|
||||||
|
this.updateBackground()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleFullScreen() {
|
toggleFullScreen() {
|
||||||
this.fullScreen = !this.fullScreen;
|
this.fullScreen = !this.fullScreen;
|
||||||
@ -61,6 +114,7 @@ export default class Rewind extends Component {
|
|||||||
@action
|
@action
|
||||||
handleScroll({ target }) {
|
handleScroll({ target }) {
|
||||||
let children = this.rewindContainer.getElementsByClassName("parallax-bg");
|
let children = this.rewindContainer.getElementsByClassName("parallax-bg");
|
||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
children[i].style.transform = `translateY(-${
|
children[i].style.transform = `translateY(-${
|
||||||
(target.scrollTop * (i + 1)) / 5
|
(target.scrollTop * (i + 1)) / 5
|
||||||
@ -80,6 +134,7 @@ export default class Rewind extends Component {
|
|||||||
(if this.fullScreen "-fullscreen")
|
(if this.fullScreen "-fullscreen")
|
||||||
}}
|
}}
|
||||||
{{didInsert this.loadRewind}}
|
{{didInsert this.loadRewind}}
|
||||||
|
{{didInsert this.preLoadImages}}
|
||||||
{{on "keydown" this.handleEscape}}
|
{{on "keydown" this.handleEscape}}
|
||||||
{{didInsert this.registerRewindContainer}}
|
{{didInsert this.registerRewindContainer}}
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
@ -87,7 +142,7 @@ export default class Rewind extends Component {
|
|||||||
|
|
||||||
<div class="rewind">
|
<div class="rewind">
|
||||||
<div class="background-1 parallax-bg"></div>
|
<div class="background-1 parallax-bg"></div>
|
||||||
<div class="background-2 parallax-bg"></div>
|
<canvas class="background-2 parallax-bg"></canvas>
|
||||||
{{#if this.loadingRewind}}
|
{{#if this.loadingRewind}}
|
||||||
<div class="rewind-loader">
|
<div class="rewind-loader">
|
||||||
<div class="spinner small"></div>
|
<div class="spinner small"></div>
|
||||||
|
@ -10,12 +10,15 @@ body {
|
|||||||
.rewind-report-container {
|
.rewind-report-container {
|
||||||
perspective: 1000px;
|
perspective: 1000px;
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
|
gap: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rewind-card__wrapper {
|
.rewind-card__wrapper {
|
||||||
|
width: 150px;
|
||||||
|
height: 100px;
|
||||||
|
perspective: 1000px;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
transform-style: preserve-3d;
|
transform-style: preserve-3d;
|
||||||
background-color: hsla(0deg, 0%, 0%, 10%);
|
|
||||||
border-radius: 0.25vw;
|
border-radius: 0.25vw;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
animation-name: ambientMovement;
|
animation-name: ambientMovement;
|
||||||
@ -24,6 +27,61 @@ body {
|
|||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
animation-timing-function: ease-in-out;
|
animation-timing-function: ease-in-out;
|
||||||
transform: rotateZ(calc(var(--ambientAmount) * 10deg));
|
transform: rotateZ(calc(var(--ambientAmount) * 10deg));
|
||||||
|
z-index: 1;
|
||||||
|
.rewind-card__inner {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transform: rotateY(0) scale(1);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
&:hover .rewind-card__inner {
|
||||||
|
animation-name: flip-zoom;
|
||||||
|
animation-direction: normal;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
animation-timing-function: cubic-bezier(0.73, 0.42, 0.03, 0.78);
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
}
|
||||||
|
.rewind-card.-back {
|
||||||
|
transform: rotateY(180deg);
|
||||||
|
}
|
||||||
|
.rewind-card.-front,
|
||||||
|
.rewind-card.-back {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-backface-visibility: hidden; /* Safari */
|
||||||
|
backface-visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes flip-zoom {
|
||||||
|
0% {
|
||||||
|
transform: rotateY(0) scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: rotateY(0) scale(0.9);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotateY(180deg) scale(1.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rewind-card {
|
||||||
|
background-color: var(--secondary);
|
||||||
|
box-shadow: 0 0 0 1px var(--primary-200);
|
||||||
|
border-radius: var(--d-border-radius);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
box-sizing: border-box;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
&__title {
|
&__title {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@ -32,30 +90,6 @@ body {
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
.rewind-card {
|
|
||||||
box-shadow: 0 0 0 1px var(--primary-200);
|
|
||||||
border-radius: var(--d-border-radius);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
padding: 1em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
background: var(--secondary);
|
|
||||||
will-change: transform;
|
|
||||||
position: relative;
|
|
||||||
transform-style: preserve-3d;
|
|
||||||
}
|
|
||||||
&:hover .rewind-card {
|
|
||||||
animation-name: easeOutElastic;
|
|
||||||
animation-duration: 400ms;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
transform: translateZ(calc(var(--easeAmount) * 50px));
|
|
||||||
box-shadow: 0 0.15em calc(0.5em + (var(--easeAmount) * 0.5em))
|
|
||||||
calc(-0.25em - (var(--easeAmount) * 0.25em)) currentColor;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes ambientMovement {
|
@keyframes ambientMovement {
|
||||||
|
@ -9,3 +9,11 @@
|
|||||||
@import "word-cloud";
|
@import "word-cloud";
|
||||||
@import "favorite-tags";
|
@import "favorite-tags";
|
||||||
@import "favorite-categories";
|
@import "favorite-categories";
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5 {
|
||||||
|
font-family: "Rubik", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
@ -1,3 +1,17 @@
|
|||||||
|
:root {
|
||||||
|
--frame-1: url(/plugins/discourse-rewind/images/bg-frames/bg-2_1.png);
|
||||||
|
--frame-2: url(/plugins/discourse-rewind/images/bg-frames/bg-2_2.png);
|
||||||
|
--frame-3: url(/plugins/discourse-rewind/images/bg-frames/bg-2_3.png);
|
||||||
|
--frame-4: url(/plugins/discourse-rewind/images/bg-frames/bg-2_4.png);
|
||||||
|
--frame-5: url(/plugins/discourse-rewind/images/bg-frames/bg-2_5.png);
|
||||||
|
--frame-6: url(/plugins/discourse-rewind/images/bg-frames/bg-2_6.png);
|
||||||
|
--frame-7: url(/plugins/discourse-rewind/images/bg-frames/bg-2_7.png);
|
||||||
|
--frame-8: url(/plugins/discourse-rewind/images/bg-frames/bg-2_8.png);
|
||||||
|
--frame-9: url(/plugins/discourse-rewind/images/bg-frames/bg-2_9.png);
|
||||||
|
--frame-10: url(/plugins/discourse-rewind/images/bg-frames/bg-2_10.png);
|
||||||
|
--frame-11: url(/plugins/discourse-rewind/images/bg-frames/bg-2_11.png);
|
||||||
|
}
|
||||||
|
|
||||||
.rewind-container {
|
.rewind-container {
|
||||||
border-radius: var(--d-border-radius);
|
border-radius: var(--d-border-radius);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@ -47,12 +61,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.background-2 {
|
.background-2 {
|
||||||
|
--frame: var(--frame-1);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 1000%;
|
height: 1000%;
|
||||||
background: url(/plugins/discourse-rewind/images/bg-2.png);
|
background: var(--frame);
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
transform: translateY(0px);
|
transform: translateY(0px);
|
||||||
|
will-change: background;
|
||||||
|
backface-visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rewind__scroll-wrapper {
|
.rewind__scroll-wrapper {
|
||||||
|
@ -1,5 +1,2 @@
|
|||||||
.rewind-report-page.-word-cloud {
|
.rewind-report-page.-word-cloud {
|
||||||
.rewind-report-container {
|
|
||||||
gap: 1em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
BIN
public/images/bg-2-2.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/images/bg-frames/bg-2_1.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/images/bg-frames/bg-2_10.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/images/bg-frames/bg-2_11.png
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
public/images/bg-frames/bg-2_2.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/images/bg-frames/bg-2_3.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/images/bg-frames/bg-2_4.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
public/images/bg-frames/bg-2_5.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
public/images/bg-frames/bg-2_6.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
public/images/bg-frames/bg-2_7.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/images/bg-frames/bg-2_8.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
public/images/bg-frames/bg-2_9.png
Normal file
After Width: | Height: | Size: 42 KiB |