mirror of
https://github.com/pomber/git-history.git
synced 2024-11-22 23:13:20 +03:00
Animate code
This commit is contained in:
parent
269542d60e
commit
2b7a3072a6
119
src/airframe/airframe.js
Normal file
119
src/airframe/airframe.js
Normal file
@ -0,0 +1,119 @@
|
||||
import easing from "./easing";
|
||||
/* eslint-disable */
|
||||
function mergeResults(results) {
|
||||
const firstResult = results[0];
|
||||
if (results.length < 2) {
|
||||
return firstResult;
|
||||
}
|
||||
if (Array.isArray(firstResult)) {
|
||||
// console.log("merge", results);
|
||||
return firstResult.map((_, i) => {
|
||||
return mergeResults(results.map(result => result[i]));
|
||||
});
|
||||
} else {
|
||||
return Object.assign({}, ...results);
|
||||
}
|
||||
}
|
||||
|
||||
const airframe = {
|
||||
parallel: ({ children: fns }) => {
|
||||
return (t, ...args) => {
|
||||
const styles = fns.map(fn => fn(t, ...args));
|
||||
const result = mergeResults(styles);
|
||||
return result;
|
||||
};
|
||||
},
|
||||
chain: ({ children: fns, durations }) => {
|
||||
return (t, ...args) => {
|
||||
let style = fns[0](0, ...args);
|
||||
let lowerDuration = 0;
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
const fn = fns[i];
|
||||
const thisDuration = durations[i];
|
||||
const upperDuration = lowerDuration + thisDuration;
|
||||
if (lowerDuration <= t && t <= upperDuration) {
|
||||
const innerT = (t - lowerDuration) / thisDuration;
|
||||
style = mergeResults([style, fn(innerT, ...args)]);
|
||||
} else if (upperDuration < t) {
|
||||
// merge the end of previous animation
|
||||
style = mergeResults([style, fn(1, ...args)]);
|
||||
} else if (t < lowerDuration) {
|
||||
// merge the start of future animation
|
||||
style = mergeResults([fn(0, ...args), style]);
|
||||
}
|
||||
lowerDuration = upperDuration;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
},
|
||||
delay: () => () => ({}),
|
||||
tween: ({ from, to, ease = easing.linear }) => (t, targets) => {
|
||||
const style = {};
|
||||
Object.keys(from).forEach(key => {
|
||||
const value = from[key] + (to[key] - from[key]) * ease(t);
|
||||
if (key === "x") {
|
||||
style["transform"] = `translateX(${value}px)`;
|
||||
} else {
|
||||
style[key] = value;
|
||||
}
|
||||
});
|
||||
return style;
|
||||
}
|
||||
};
|
||||
|
||||
/* @jsx createAnimation */
|
||||
export const Stagger = props => (t, targets) => {
|
||||
const filter = target => !props.filter || props.filter(target);
|
||||
const interval =
|
||||
targets.filter(filter).length < 2
|
||||
? 0
|
||||
: props.interval / (targets.filter(filter).length - 1);
|
||||
let i = 0;
|
||||
return targets.map(target => {
|
||||
// console.log(target, props.filter(target));
|
||||
if (!filter(target)) {
|
||||
return {};
|
||||
}
|
||||
const animation = (
|
||||
<parallel>
|
||||
<chain durations={[i * interval, 1 - props.interval]}>
|
||||
<delay />
|
||||
{props.children[0]}
|
||||
</chain>
|
||||
</parallel>
|
||||
);
|
||||
i++;
|
||||
const result = animation(t, target);
|
||||
// console.log("Stagger Result", t, result);
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
export function createAnimation(type, props, ...children) {
|
||||
const allProps = Object.assign({ children }, props);
|
||||
if (typeof type === "string") {
|
||||
if (window.LOG === "verbose") {
|
||||
return (t, ...args) => {
|
||||
console.groupCollapsed(type, t);
|
||||
const result = airframe[type](allProps)(t, ...args);
|
||||
console.log(result);
|
||||
console.groupEnd();
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
return airframe[type](allProps);
|
||||
}
|
||||
} else {
|
||||
if (window.LOG === "verbose") {
|
||||
return (t, ...args) => {
|
||||
console.groupCollapsed(type.name, t);
|
||||
const result = type(allProps)(t, ...args);
|
||||
console.log(result);
|
||||
console.groupEnd();
|
||||
return result;
|
||||
};
|
||||
} else {
|
||||
return type(allProps);
|
||||
}
|
||||
}
|
||||
}
|
54
src/airframe/easing.js
Normal file
54
src/airframe/easing.js
Normal file
@ -0,0 +1,54 @@
|
||||
export default {
|
||||
// no easing, no acceleration
|
||||
linear: function(t) {
|
||||
return t;
|
||||
},
|
||||
// accelerating from zero velocity
|
||||
easeInQuad: function(t) {
|
||||
return t * t;
|
||||
},
|
||||
// decelerating to zero velocity
|
||||
easeOutQuad: function(t) {
|
||||
return t * (2 - t);
|
||||
},
|
||||
// acceleration until halfway, then deceleration
|
||||
easeInOutQuad: function(t) {
|
||||
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
||||
},
|
||||
// accelerating from zero velocity
|
||||
easeInCubic: function(t) {
|
||||
return t * t * t;
|
||||
},
|
||||
// decelerating to zero velocity
|
||||
easeOutCubic: function(t) {
|
||||
return --t * t * t + 1;
|
||||
},
|
||||
// acceleration until halfway, then deceleration
|
||||
easeInOutCubic: function(t) {
|
||||
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
|
||||
},
|
||||
// accelerating from zero velocity
|
||||
easeInQuart: function(t) {
|
||||
return t * t * t * t;
|
||||
},
|
||||
// decelerating to zero velocity
|
||||
easeOutQuart: function(t) {
|
||||
return 1 - --t * t * t * t;
|
||||
},
|
||||
// acceleration until halfway, then deceleration
|
||||
easeInOutQuart: function(t) {
|
||||
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
|
||||
},
|
||||
// accelerating from zero velocity
|
||||
easeInQuint: function(t) {
|
||||
return t * t * t * t * t;
|
||||
},
|
||||
// decelerating to zero velocity
|
||||
easeOutQuint: function(t) {
|
||||
return 1 + --t * t * t * t * t;
|
||||
},
|
||||
// acceleration until halfway, then deceleration
|
||||
easeInOutQuint: function(t) {
|
||||
return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
|
||||
}
|
||||
};
|
78
src/animation.js
Normal file
78
src/animation.js
Normal file
@ -0,0 +1,78 @@
|
||||
import { createAnimation, Stagger } from "./airframe/airframe";
|
||||
import easing from "./airframe/easing";
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
const dx = 250;
|
||||
|
||||
/* @jsx createAnimation */
|
||||
|
||||
// window.LOG = "verbose";
|
||||
|
||||
const SlideToLeft = () => (
|
||||
<tween
|
||||
from={{ x: 0, opacity: 1 }}
|
||||
to={{ x: -dx, opacity: 0 }}
|
||||
ease={easing.easeInQuad}
|
||||
/>
|
||||
);
|
||||
|
||||
function ShrinkHeight() {
|
||||
return (
|
||||
<tween
|
||||
from={{ height: 15 }}
|
||||
to={{ height: 0 }}
|
||||
ease={easing.easeInOutQuad}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const SlideFromRight = () => (
|
||||
<tween
|
||||
from={{ x: dx, opacity: 0 }}
|
||||
to={{ x: 0, opacity: 1 }}
|
||||
ease={easing.easeOutQuad}
|
||||
/>
|
||||
);
|
||||
function GrowHeight() {
|
||||
return (
|
||||
<tween
|
||||
from={{ height: 0 }}
|
||||
to={{ height: 15 }}
|
||||
ease={easing.easeInOutQuad}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function SwitchLines({ filterExit, filterEnter }) {
|
||||
return (
|
||||
<parallel>
|
||||
<Stagger interval={0.2} filter={filterExit}>
|
||||
<chain durations={[0.35, 0.3, 0.35]}>
|
||||
<SlideToLeft />
|
||||
<ShrinkHeight />
|
||||
</chain>
|
||||
</Stagger>
|
||||
<Stagger interval={0.2} filter={filterEnter}>
|
||||
<chain durations={[0.35, 0.3, 0.35]}>
|
||||
<delay />
|
||||
<GrowHeight />
|
||||
<SlideFromRight />
|
||||
</chain>
|
||||
</Stagger>
|
||||
</parallel>
|
||||
);
|
||||
}
|
||||
|
||||
export default (
|
||||
<chain durations={[0.5, 0.5]}>
|
||||
<SwitchLines
|
||||
filterExit={line => line.left && !line.middle}
|
||||
filterEnter={line => !line.left && line.middle}
|
||||
/>
|
||||
<SwitchLines
|
||||
filterExit={line => line.middle && !line.right}
|
||||
filterEnter={line => !line.middle && line.right}
|
||||
/>
|
||||
</chain>
|
||||
);
|
@ -83,61 +83,3 @@ export function getSlides(codes) {
|
||||
.filter(line => line.middle || line.left || line.right);
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
function getLines(change) {
|
||||
return change.value
|
||||
.trimRight(newlineRe)
|
||||
.split(newlineRe)
|
||||
.map(content => ({
|
||||
content
|
||||
}));
|
||||
}
|
||||
|
||||
export function tripleDiff(left, middle, right) {
|
||||
const leftDiff = diff.diffLines(left, middle);
|
||||
const rightDiff = diff.diffLines(middle, right);
|
||||
|
||||
let x = leftDiff
|
||||
.map(change =>
|
||||
change.value
|
||||
.trimRight(newlineRe)
|
||||
.split(newlineRe)
|
||||
.map(content => ({
|
||||
content,
|
||||
left: !change.added,
|
||||
middle: !change.removed
|
||||
}))
|
||||
)
|
||||
.flat();
|
||||
// console.log(JSON.stringify(leftDiff, null, 2));
|
||||
// console.log(JSON.stringify(x, null, 2));
|
||||
|
||||
let i = 0;
|
||||
// console.log(rightDiff);
|
||||
rightDiff.forEach(change => {
|
||||
let mx = x.filter(l => l.middle || l.right);
|
||||
if (change.added) {
|
||||
const lines = change.value
|
||||
.trimRight(newlineRe)
|
||||
.split(newlineRe)
|
||||
.map(content => ({
|
||||
content,
|
||||
left: false,
|
||||
middle: false,
|
||||
right: true
|
||||
}));
|
||||
insert(x, i, lines);
|
||||
} else if (!change.removed) {
|
||||
// console.log(change);
|
||||
for (let j = 0; j < change.count; j++) {
|
||||
mx[i + j].right = true;
|
||||
}
|
||||
}
|
||||
i += change.count;
|
||||
});
|
||||
// console.log(JSON.stringify(rightDiff, null, 2));
|
||||
// console.log(JSON.stringify(x, null, 2));
|
||||
return x;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { getSlides } from "./differ";
|
||||
import { useSpring } from "react-use";
|
||||
import Slide from "./slide";
|
||||
|
||||
export default function History({ commits, language }) {
|
||||
const codes = commits.map(commit => commit.content);
|
||||
@ -22,7 +23,11 @@ export default function History({ commits, language }) {
|
||||
}
|
||||
};
|
||||
});
|
||||
return <pre>{codes[index]}</pre>;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Slide time={current - index} lines={slideLines[index]} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
function useSliderSpring(initial) {
|
||||
const [target, setTarget] = useState(initial);
|
||||
|
@ -6,6 +6,8 @@ body {
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: rgb(1, 22, 39);
|
||||
color: rgb(214, 222, 235);
|
||||
}
|
||||
|
||||
code {
|
||||
|
107
src/nightOwl.js
Normal file
107
src/nightOwl.js
Normal file
@ -0,0 +1,107 @@
|
||||
const theme = {
|
||||
plain: {
|
||||
color: "#d6deeb",
|
||||
backgroundColor: "#011627"
|
||||
},
|
||||
styles: [
|
||||
{
|
||||
types: ["changed"],
|
||||
style: {
|
||||
color: "rgb(162, 191, 252)",
|
||||
fontStyle: "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["deleted"],
|
||||
style: {
|
||||
color: "rgba(239, 83, 80, 0.56)",
|
||||
fontStyle: "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["inserted", "attr-name"],
|
||||
style: {
|
||||
color: "rgb(173, 219, 103)",
|
||||
fontStyle: "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["comment"],
|
||||
style: {
|
||||
color: "rgb(99, 119, 119)",
|
||||
fontStyle: "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["string", "url"],
|
||||
style: {
|
||||
color: "rgb(173, 219, 103)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["variable"],
|
||||
style: {
|
||||
color: "rgb(214, 222, 235)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["number"],
|
||||
style: {
|
||||
color: "rgb(247, 140, 108)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["builtin", "char", "constant", "function"],
|
||||
style: {
|
||||
color: "rgb(130, 170, 255)"
|
||||
}
|
||||
},
|
||||
{
|
||||
// This was manually added after the auto-generation
|
||||
// so that punctuations are not italicised
|
||||
types: ["punctuation"],
|
||||
style: {
|
||||
color: "rgb(199, 146, 234)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["selector", "doctype"],
|
||||
style: {
|
||||
color: "rgb(199, 146, 234)",
|
||||
fontStyle: "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["class-name"],
|
||||
style: {
|
||||
color: "rgb(255, 203, 139)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["tag", "operator", "keyword"],
|
||||
style: {
|
||||
color: "rgb(127, 219, 202)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["boolean"],
|
||||
style: {
|
||||
color: "rgb(255, 88, 116)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["property"],
|
||||
style: {
|
||||
color: "rgb(128, 203, 196)"
|
||||
}
|
||||
},
|
||||
{
|
||||
types: ["namespace"],
|
||||
style: {
|
||||
color: "rgb(178, 204, 214)"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
module.exports = theme;
|
34
src/slide.js
Normal file
34
src/slide.js
Normal file
@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import animation from "./animation";
|
||||
import theme from "./nightOwl";
|
||||
|
||||
export default function Slide({ time, lines }) {
|
||||
const styles = animation((time + 1) / 2, lines);
|
||||
return (
|
||||
<pre
|
||||
style={{
|
||||
backgroundColor: theme.plain.backgroundColor,
|
||||
color: theme.plain.color,
|
||||
overflow: "hidden",
|
||||
maxWidth: "978px",
|
||||
margin: "auto",
|
||||
padding: "10px"
|
||||
}}
|
||||
>
|
||||
{lines.map((line, i) => (
|
||||
<div
|
||||
style={Object.assign(
|
||||
{ overflow: "hidden", height: "15px" },
|
||||
styles[i]
|
||||
)}
|
||||
key={line.key}
|
||||
>
|
||||
{line.tokens.map(token => {
|
||||
const props = theme.styles.find(s => s.types.includes(token.type));
|
||||
return <span {...props}>{token.content}</span>;
|
||||
})}
|
||||
</div>
|
||||
))}
|
||||
</pre>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user