protocol/packages/website/ts/components/background_marquee.tsx
2019-05-28 12:58:42 -07:00

151 lines
3.8 KiB
TypeScript

import React from 'react';
import styled, { keyframes } from 'styled-components';
const scrollFactory = (height: number, imgRepeatCt: number) => keyframes`
0% { transform: translate3d(0, -${height}px, 0) }
100% { transform: translate3d(0, -${height * (imgRepeatCt - 1)}px, 0) }
`;
const scrollMobileFactory = (height: number, imgRepeatCt: number) => keyframes`
0% { transform: translate3d(0, -${height}px, 0) }
100% { transform: translate3d(0, -${height * (imgRepeatCt - 1)}px, 0) }
`;
interface MarqueeWrapProps {
height?: string;
imgHeightInPx: number;
imgRepeatCt: number;
}
const MarqueeWrap = styled.div<MarqueeWrapProps>`
width: 130%;
margin-left: -15%;
margin-right: -15%;
height: ${props => props.height || '100%'};
overflow: hidden;
position: relative;
&:after {
content: '';
position: absolute;
width: 100%;
height: ${props => props.height || '100%'};
left: 0;
top: 0;
background: linear-gradient(180.18deg, #000000 11.09%, rgba(0, 0, 0, 0.8) 62.74%, rgba(0, 0, 0, 0) 103.8%);
}
&:before {
content: '';
position: absolute;
height: 4rem;
left: -2rem;
right: -2rem;
bottom: -2rem;
filter: blur(1rem);
z-index: 1;
background: #000;
}
@media (max-width: 768px) {
overflow: hidden;
}
> div {
height: auto;
display: flex;
flex-direction: column;
will-change: transform;
transform: translate3d(0, -${props => props.imgHeightInPx}px, 0);
}
@media (min-width: 768px) {
> div {
height: ${props => props.imgHeightInPx * props.imgRepeatCt}px;
animation: ${props => scrollFactory(props.imgHeightInPx, props.imgRepeatCt)} 140s linear infinite;
}
}
@media (max-width: 768px) {
> div {
height: ${props => props.imgHeightInPx * props.imgRepeatCt}px;
animation: ${props => scrollMobileFactory(props.imgHeightInPx, props.imgRepeatCt)} 140s linear infinite;
}
}
`;
interface CardProps {}
const Card = styled.div<CardProps>`
opacity: 1;
will-change: opacity, transform;
& + & {
margin-top: -0px;
}
img {
height: auto;
}
@media (min-width: 768px) {
img {
width: 100%;
}
}
@media (max-width: 768px) {
img {
width: 100%;
}
}
`;
const MarqueeImg = styled.img`
object-fit: cover;
opacity: 1;
`;
export interface BackgroundMarqueeProps {
height?: string;
imgSrcUrl: string;
}
export class BackgroundMarquee extends React.Component<BackgroundMarqueeProps> {
public state = {
imgRepeatCt: 3,
imgHeightInPx: 2000,
};
private _imageRef: HTMLImageElement;
public render(): React.ReactNode {
return (
<MarqueeWrap
height={this.props.height}
imgHeightInPx={this.state.imgHeightInPx}
imgRepeatCt={this.state.imgRepeatCt}
>
<div>
{[...Array(this.state.imgRepeatCt)].map((item, index) => (
<Card key={`card-${index}`}>
<MarqueeImg
onLoad={index === 0 ? this._onImageUpdate : null}
ref={ref => (index === 0 ? (this._imageRef = ref) : null)}
src={this.props.imgSrcUrl}
alt=""
/>
</Card>
))}
</div>
</MarqueeWrap>
);
}
private readonly _onImageUpdate = () => {
if (!!this._imageRef) {
this.setState({ imgHeightInPx: this._imageRef.clientHeight });
}
};
}