ReactMotion - Mouse dependent StaggeredMotion
Follow first, last or middle item. Also normalize the mouse position to get reasonable values to use for scaling of letters.
by jonahe
HTML
<script src="https://unpkg.com/react-motion/build/react-motion.js"></script>
<div id="app"></div>
CSS
body {
background: teal;
padding: 10px;
height: 100vh;
box-sizing: border-box;
font-family: Helvetica;
}
#app {
display: flex;
flex-direction: column;
background: #fff;
border-radius: 4px;
padding: 10px;
height: 100%;
box-sizing: border-box;
}
.animation-container {
display: flex;
}
.char-element {
font-size: 40px;
white-space: pre-wrap;
user-select: none;
transition: color 1s;
}
React
const {StaggeredMotion, spring, presets} = ReactMotion;
const SPRING_INITIAL_OFFSET_X = 50;
const SPRING_INITIAL_OFFSET_Y = -70;
const SPRING_DESTINATION_X = 0;
const SPRING_DESTINATION_Y = 0;
// SO copy paste
function normalize(min, max) {
var delta = max - min;
return function (val) {
return (val - min) / delta;
};
}
// let all other values move toward the first, last or middle value
const FOLLOW_MODES = {
FIRST : "FIRST",
LAST : "LAST",
MIDDLE : "MIDDLE"
};
const FOLLOW_MODE = FOLLOW_MODES.MIDDLE;
const CONTENT_TO_ANIMATE = "Try moving the mouse around";
const SPLITTING_MODES = {
ON_WORD : /\b/, // keeps white-space, so "hello world" -> ["hello", " ", "world"]
ON_EVERY_CHAR : "",
NO_SPLIT : "[\s\S]*", // regex that matches everything
};
const SPLITTING_FUNCTION = content => content.split(SPLITTING_MODES.ON_EVERY_CHAR);
const paddingToSubtract = 40;
const normalizedMouseYpos = normalize(1, document.getElementById('app').getBoundingClientRect().height - paddingToSubtract);
const getInitialStylesFn = (itemsToAnimate) => (mouseX, mouseY) => {
return itemsToAnimate.map(() => ({
x: mouseX,
y: mouseY,
})
)};
// available gentle, wobbly, stiff
// /*
const myPreset = Object.assign({}, presets.stiff, {precision: 1})
const mySpring = springTo => spring(springTo,myPreset);
/* use this if you want to follow first or last item */
const prevToNextStylesTransform = (followLast) => (mouseX, mouseY) => (prevStyles) => {
const nextStyles = prevStyles.map((prevStyle, i) => {
if(followLast ? (i == prevStyles.length - 1) : i == 0) {
return {
x: mySpring(mouseX < 30 ? 0 : mouseX - 30),
y: mySpring(mouseY < 50 ? 0 : mouseY - 50),
};
} else {
const nearestSibling = prevStyles[i + (followLast ? 1 : -1)];
const {x: siblingX, y: siblingY} = nearestSibling;
return { x: mySpring(siblingX), y: mySpring(siblingY) };
}
});
return nextStyles;
};
/* use this to follow the middle one...