feat: lower presets

added clean lower with no styling and minimal animation
This commit is contained in:
cv
2021-05-05 10:48:51 +02:00
parent abc0bfb3dc
commit 2b9c5ffc95
6 changed files with 277 additions and 39 deletions
+3 -1
View File
@@ -17,7 +17,9 @@ const StageManager = lazy(() =>
import('./features/viewers/backstage/StageManager')
);
const Public = lazy(() => import('./features/viewers/foh/Public'));
const Lower = lazy(() => import('./features/viewers/production/Lower'));
const Lower = lazy(() =>
import('./features/viewers/production/lower/LowerWrapper')
);
const Pip = lazy(() => import('./features/viewers/production/Pip'));
const queryClient = new QueryClient();
@@ -0,0 +1,131 @@
import { AnimatePresence, motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import style from './LowerClean.module.css';
export default function LowerClean(props) {
const { lower, title, options } = props;
const defaults = {
size: 1,
transitionIn: 3,
textColour: '#fffffa',
posX: 50,
posY: 700,
};
const [showLower, setShowLower] = useState(true);
// Unmount if fadeOut
useEffect(() => {
if (!options.fadeOut) return;
// Calculate time
const fadeOutTime =
(parseInt(options.fadeOut) +
(options.transitionIn || defaults.transitionIn)) *
1000;
if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => {
setShowLower(false);
}, fadeOutTime);
return () => clearTimeout(timeout);
}, [options.fadeOut, options.transitionIn, defaults.transitionIn]);
// calculate transition times
useEffect(() => {});
// Format messages
const showLowerMessage = lower.text !== '' && lower.visible;
// motion
// transition segments
const t = options.transitionIn || defaults.transitionIn;
const quarter = t / 4;
const third = t / 3;
const half = t / 2;
const lowerThirdVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
duration: third,
},
},
exit: {
opacity: 0,
transition: {
duration: third,
},
},
};
const titleVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
delay: quarter,
duration: half,
},
},
};
const sizeMultiplier = (options.size || 1) * 4;
return (
<div
className={style.lowerThird}
style={{
backgroundColor: options.keyColour || defaults.keyColour,
color: options.textColour || defaults.textColour,
fontSize: `${sizeMultiplier}vh`,
}}
>
<AnimatePresence>
{showLower && (
<motion.div
className={style.lowerContainer}
style={{
backgroundColor: options.bgColour || defaults.bgColour,
top: options.posY || defaults.posY,
left: options.posX || defaults.posX,
}}
variants={lowerThirdVariants}
initial='hidden'
animate='visible'
exit='exit'
>
<motion.div className={style.title} variants={titleVariants}>
{title.titleNow}
</motion.div>
<motion.div className={style.subtitle} variants={titleVariants}>
{title.presenterNow}
</motion.div>
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{showLowerMessage && (
<motion.div
className={style.messageContainer}
style={{
backgroundColor: options.bgColour || defaults.bgColour,
}}
key='modal'
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ scaleY: 0, opacity: 0 }}
transition={{ duration: 0.5 }}
>
<div className={style.message}>{lower.text}</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
@@ -0,0 +1,27 @@
.lowerThird {
margin: 0;
box-sizing: border-box; /* reset */
overflow: hidden;
width: 100%; /* restrict the page width to viewport */
height: 100%;
color: #fffffa;
font-size: 4vh;
}
.lowerContainer {
position: absolute;
padding: 1vh 2vh;
border-radius: 1vh;
}
.messageContainer {
position: absolute;
bottom: 2vh;
width: 100%;
text-align: center;
}
.message {
font-size: 3.5vh;
}
@@ -1,36 +1,26 @@
import { AnimatePresence, motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import style from './Lower.module.css';
import style from './LowerLines.module.css';
export default function Lower(props) {
const { lower, title, time, general } = props;
export default function LowerLines(props) {
const { lower, title, options } = props;
const defaults = {
size: 1,
transitionIn: 3,
textColour: '#fffffa',
bgColour: '#00000033',
};
const [showLower, setShowLower] = useState(true);
// Set window title
// Unmount if fadeOut
useEffect(() => {
document.title = 'ontime - Lower Thirds';
}, []);
// TODO: sanitize data
// getting config from URL: preset, size, transition, bg, text, key
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
let params = new URLSearchParams(props.location.search);
const preset = params.get('preset') ? params.get('preset') : 1;
const size = params.get('size') ? params.get('size') : 1;
const transitionIn = params.get('transition') ? params.get('transition') : 3;
const textColour = params.get('text') ? `#${params.get('text')}` : '#fffffa';
const bgColour = params.get('bg') ? `#${params.get('bg')}` : '#00000033';
const key = params.get('key') ? `#${params.get('key')}` : 'null';
const fadeOut = params.get('fadeout') ? `${params.get('fadeout')}` : 'null';
useEffect(() => {
if (!fadeOut) return;
if (!options.fadeOut) return;
// Calculate time
let transitionTime = parseInt(transitionIn) || 0;
let fadeOutTime = (parseInt(fadeOut) + transitionTime) * 1000;
const fadeOutTime =
(parseInt(options.fadeOut) +
(options.transitionIn || defaults.transitionIn)) *
1000;
if (isNaN(fadeOutTime)) return;
const timeout = setTimeout(() => {
@@ -38,18 +28,19 @@ export default function Lower(props) {
}, fadeOutTime);
return () => clearTimeout(timeout);
}, []);
}, [options.fadeOut, options.transitionIn, defaults.transitionIn]);
// Format messages
const showLowerMessage = lower.text !== '' && lower.visible;
// transition segments
const eight = transitionIn / 8;
const quarter = transitionIn / 4;
const third = transitionIn / 3;
const half = transitionIn / 2;
// motion
// transition segments
const t = options.transitionIn || defaults.transitionIn;
const eight = t / 8;
const quarter = t / 4;
const third = t / 3;
const half = t / 2;
const lowerThirdVariants = {
hidden: {
opacity: 0,
@@ -120,20 +111,22 @@ export default function Lower(props) {
},
};
const sizeMultiplier = (options.size || 1) * 4;
return (
<div
className={style.lowerThird}
style={{
backgroundColor: key,
color: textColour,
fontSize: `${size * 4}vh`,
backgroundColor: options.keyColour || defaults.keyColour,
color: options.textColour || defaults.textColour,
fontSize: `${sizeMultiplier}vh`,
}}
>
<AnimatePresence>
{showLower && (
<motion.div
className={style.lowerContainer}
style={{ backgroundColor: bgColour }}
style={{ backgroundColor: options.bgColour || defaults.bgColour }}
variants={lowerThirdVariants}
initial='hidden'
animate='visible'
@@ -21,8 +21,7 @@
border-radius: 0 1vh 1vh 0;
}
.messageContainer,
.messageContainerHidden {
.messageContainer {
position: absolute;
bottom: 2vh;
width: 100%;
@@ -0,0 +1,86 @@
import { useEffect, useState } from 'react';
import LowerClean from './LowerClean';
import LowerLines from './LowerLines';
export default function Lower(props) {
const [preset, setPreset] = useState(1);
const [lowerOptions, setLowerOptions] = useState({});
// Set window title
useEffect(() => {
document.title = 'ontime - Lower Thirds';
}, []);
// TODO: sanitize data
// getting config from URL: preset, size, transition, bg, text, key
// eg. http://localhost:3000/lower?bg=ff2&text=f00&size=0.6&transition=5
// Check for user options
useEffect(() => {
// get parameters
const params = new URLSearchParams(props.location.search);
// create aux
let options = {};
// preset: selector
// Should be a number 1-n
let p = parseInt(params.get('preset'));
if (!isNaN(p)) setPreset(p);
// size: multiplier
// Should be a number 0.0-n
let s = params.get('size');
if (s) options.size = s;
// transitionIn: seconds
// Should be a number 0-n
let t = parseInt(params.get('transition'));
if (!isNaN(t)) options.transitionIn = t;
// textColour: string
// Should be a hex string '#ffffff'
let c = params.get('text');
if (c) options.textColour = `#${c}`;
// bgColour: string
// Should be a hex string '#ffffff'
let b = params.get('bg');
if (b) options.bgColour = `#${b}`;
// key: string
// Should be a hex string '#00FF00' with key colour
let k = params.get('key');
if (k) options.keyColour = `#${k}`;
// fadeOut: seconds
// Should be a number 0-n
let f = parseInt(params.get('fadeout'));
if (!isNaN(f)) options.fadeOut = f;
// x: pixels
// Should be a number 0-n
let x = parseInt(params.get('x'));
if (!isNaN(x)) options.posX = x;
// y: pixels
// Should be a number 0-n
let y = parseInt(params.get('y'));
if (!isNaN(y)) options.posY = y;
setLowerOptions({
...options,
set: true,
});
}, [props.location.search]);
// Defer rendering until we have data ready
if (!lowerOptions.set) return null;
switch (preset) {
case 0:
return <LowerClean {...props} options={lowerOptions} />;
case 1:
return <LowerLines {...props} options={lowerOptions} />;
default:
return <LowerLines {...props} options={lowerOptions} />;
}
}