Reputation: 630
I have a simple JS script which listens to keyboard input and displays, at a random position, a short animation of every typed letter fading out and getting smaller.
'use strict'
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
document.addEventListener('keydown', function(e) {
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
.anim {
position: fixed;
text-align: center;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0%;
}
@keyframes fade {
0% { opacity: 100%; font-size: 300px;}
100% { opacity: 0%; font-size: 100px;}
}
By animating the font-size
property, each letter gets smaller. However, since its "anchor point" is the top of the div, the visible effect is a letter getting smaller and moving slightly upwards. I would like each letter to shrink towards the vertical center of the div
instead.
I can calculate the center the div
easily and add the proper top
coordinate to the @keyframe
property, but I don't know how to modify that property in JS, individually for each div
. Is this possible at all via CSS? Or should I rewrite the whole thing in pure JS?
Upvotes: 0
Views: 475
Reputation: 1364
You don't need to adjust the div's top value at all. As there is no border or anything else displayed for the DIV tag itself - just the letter within it - you can adjust either the margin, the border and/or the padding to achieve the same effect as increasing the top value for the DIV. As each of these can be handled within the css transition, you could do something like:
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
document.addEventListener('keydown', function(e) {
const body = document.querySelector('body')
const ignoreKeys = [
'Alt', 'Shift', 'Control', 'CapsLock', 'Tab', 'Backspace', 'Escape', 'Meta',
'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'
]
if (!ignoreKeys.includes(e.key)) {
// Values 400 & 200 keep the div completely inside the window
const maxHeight = window.innerHeight - 400
const maxWidth = window.innerWidth - 200
const div = document.createElement('div')
div.className = 'anim'
div.textContent = e.key;
div.style.top = getRandomInt(0, maxHeight) + 'px'
div.style.left = getRandomInt(0, maxWidth) + 'px'
body.append(div)
setTimeout(function() { div.remove() }, 3000)
}
})
.anim {
display:block;
position: fixed;
text-align: center;
width:200px;
animation-name: fade;
animation-duration: 2s;
animation-timing-function: ease-in-out;
opacity: 0;
margin:0px;
}
@keyframes fade {
0% { opacity:1; font-size: 300px;}
100% { opacity:0; font-size: 100px; margin-top:100px;}
}
The initial state of the DIV is with margin:0px. Adding a margin-top setting to the keyframes css, increases this from 0 to 100 during the transition. The effect of that is to push the DIV down - and, as noted above, as nothing is being displayed for the DIV itself, the user will not see it move. Note that I have fixed the width of the DIV at 200px so ensure that everything is always centered horizontally - otherwise the DIV width is based on the width of the character, so would change during transition and the character would move to the left as the centre line changes. I've moved some of the code around to make it easier to test - but the only actual change is in the CSS styling. Also note that opacity is a value from 0 to 1, so should not be shown as a percentage.
UPDATE
Have a look at the following snippet. I think that it may be possible to have random font sizes AND random positions using transform
rather than animate
.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
function zoomOUT(){
let d = document.getElementById("test");
d.classList.remove("zoomIN");
d.classList.add("zoomOUT");
}
function zoomIN(){
let d = document.getElementById("test");
let dletter = document.getElementById("testletter");
let t = getRandomInt(20, 60) * 10;
let l = getRandomInt(20, 100) * 10;
let fs = getRandomInt(10, 20) * 10;
dletter.innerHTML = letters[getRandomInt(0, 61)];
d.style.top = t + "px";
d.style.left = l + "px";
d.style.fontSize = fs + "%";
d.classList.remove("zoomOUT");
d.classList.add("zoomIN");
}
#test {
position:absolute;
padding: 50px;
margin: 0 auto;
text-align:center;
vertical-align:middle;
}
.zoomIN {
opacity:1;
transform: scale(3);
transition: transform 2s;
}
.zoomOUT {
opacity:0.5;
transform: scale(0.1);
transition: transform 3s;
}
<button onclick="zoomOUT();" z-index=1>Play</button><button onclick="zoomIN();" z-index=1>Restart</button>
<div id="test" class="zoomIN" style="top:300px; left:300px;" z-index=0><div id="testletter" style="font-size:600%; width:100%; height:100%">A</div></div>
Transform seems to keep things in the same place, so there is no need to adjust any top/margin/border/padding settings at all. In fact, the only things that change are the font-size (using scale(..)
) and opacity. The size of the font is determined by the code. Note that this requires the character to be in a div within a div. This is just a test, but should give you enough to convert things into your code requirements.
Upvotes: 1