Sfoglia il codice sorgente

Make Preview Elements Resize Accordingly To Preview Width

Francesco Baccetti 4 anni fa
parent
commit
df72f4e96e

+ 11 - 8
src/shared/components/VideoPreview/VideoPreview.styles.tsx

@@ -16,6 +16,10 @@ type ChannelProps = {
   channelClickable: boolean
 }
 
+type ScalesWithCoverProps = {
+  scalingFactor: number
+}
+
 export const CoverImage = styled.img<CoverImageProps>`
   display: block;
   position: absolute;
@@ -87,23 +91,22 @@ export const StyledAvatar = styled(Avatar)<ChannelProps>`
   cursor: ${({ channelClickable }) => (channelClickable ? 'pointer' : 'auto')};
 `
 
-export const TitleHeader = styled.h3<MainProps>`
+export const TitleHeader = styled.h3<MainProps & ScalesWithCoverProps>`
   margin: 0;
   font-weight: ${typography.weights.bold};
-  font-size: ${typography.sizes.h6};
+  font-size: calc(${(props) => props.scalingFactor} * ${typography.sizes.h6});
   ${({ main }) => main && fluidRange({ prop: 'fontSize', fromSize: '24px', toSize: '40px' })};
   line-height: ${({ main }) => (main ? 1 : 1.25)};
-  color: ${colors.white};
-  display: inline-block;
 `
 
-export const ChannelName = styled.span<ChannelProps>`
-  font-size: ${typography.sizes.subtitle2};
+export const ChannelName = styled.span<ChannelProps & ScalesWithCoverProps>`
+  font-size: calc(${(props) => props.scalingFactor} * ${typography.sizes.subtitle2});
   line-height: 1.25rem;
   display: inline-block;
   cursor: ${({ channelClickable }) => (channelClickable ? 'pointer' : 'auto')};
 `
 
-export const MetaText = styled.span<MainProps>`
-  font-size: ${({ main }) => (main ? typography.sizes.h6 : typography.sizes.subtitle2)};
+export const MetaText = styled.span<MainProps & ScalesWithCoverProps>`
+  font-size: ${({ main, scalingFactor }) =>
+    main ? typography.sizes.h6 : `calc(${scalingFactor}*${typography.sizes.subtitle2})`};
 `

+ 34 - 5
src/shared/components/VideoPreview/VideoPreview.tsx

@@ -1,4 +1,6 @@
-import React from 'react'
+import React, { useState } from 'react'
+import useResizeObserver from 'use-resize-observer'
+
 import {
   ChannelName,
   CoverDurationOverlay,
@@ -48,7 +50,6 @@ const VideoPreview: React.FC<VideoPreviewProps> = ({
   showChannel = true,
   showMeta = true,
   main = false,
-  imgRef,
   onClick,
   onChannelClick,
   className,
@@ -71,6 +72,25 @@ const VideoPreview: React.FC<VideoPreviewProps> = ({
     onClick(e)
   }
 
+  const MIN_COVER_WIDTH = 300
+  const MAX_COVER_WIDTH = 600
+  const MIN_SCALING_FACTOR = 1
+  const MAX_SCALING_FACTOR = 1.375
+  // Linear Interpolation, see https://en.wikipedia.org/wiki/Linear_interpolation
+  const calculateScalingFactor = (coverWidth: number) =>
+    MIN_SCALING_FACTOR +
+    ((coverWidth - MIN_COVER_WIDTH) * (MAX_SCALING_FACTOR - MIN_SCALING_FACTOR)) / (MAX_COVER_WIDTH - MIN_COVER_WIDTH)
+
+  const [scalingFactor, setScalingFactor] = useState(MIN_SCALING_FACTOR)
+  const { ref: imgRef } = useResizeObserver<HTMLImageElement>({
+    onResize: (size) => {
+      const { width: coverWidth } = size
+      if (coverWidth && !main) {
+        setScalingFactor(calculateScalingFactor(coverWidth))
+      }
+    },
+  })
+
   const coverNode = (
     <>
       <CoverImage src={posterURL} ref={imgRef} alt={`${title} by ${channelName} thumbnail`} />
@@ -86,7 +106,11 @@ const VideoPreview: React.FC<VideoPreviewProps> = ({
     </>
   )
 
-  const titleNode = <TitleHeader main={main}>{title}</TitleHeader>
+  const titleNode = (
+    <TitleHeader main={main} scalingFactor={scalingFactor}>
+      {title}
+    </TitleHeader>
+  )
 
   const channelAvatarNode = (
     <StyledAvatar
@@ -99,12 +123,16 @@ const VideoPreview: React.FC<VideoPreviewProps> = ({
   )
 
   const channelNameNode = (
-    <ChannelName channelClickable={channelClickable} onClick={handleChannelClick}>
+    <ChannelName channelClickable={channelClickable} onClick={handleChannelClick} scalingFactor={scalingFactor}>
       {channelName}
     </ChannelName>
   )
 
-  const metaNode = <MetaText main={main}>{formatVideoViewsAndDate(views, createdAt, { fullViews: main })}</MetaText>
+  const metaNode = (
+    <MetaText main={main} scalingFactor={scalingFactor}>
+      {formatVideoViewsAndDate(views, createdAt, { fullViews: main })}
+    </MetaText>
+  )
 
   return (
     <VideoPreviewBase
@@ -118,6 +146,7 @@ const VideoPreview: React.FC<VideoPreviewProps> = ({
       metaNode={metaNode}
       onClick={onClick && handleClick}
       className={className}
+      scalingFactor={scalingFactor}
     />
   )
 }

+ 8 - 4
src/shared/components/VideoPreview/VideoPreviewBase.styles.tsx

@@ -13,6 +13,10 @@ type ContainerProps = {
   clickable: boolean
 } & MainProps
 
+type ScalesWithCoverProps = {
+  scalingFactor: number
+}
+
 export const MAX_VIDEO_PREVIEW_WIDTH = '320px'
 
 const fadeIn = keyframes`
@@ -93,10 +97,10 @@ export const InfoContainer = styled.div<MainProps>`
   ${({ main }) => main && mainInfoContainerCss};
 `
 
-export const AvatarContainer = styled.div`
-  width: 40px;
-  min-width: 40px;
-  height: 40px;
+export const AvatarContainer = styled.div<ScalesWithCoverProps>`
+  width: calc(40px * ${(props) => props.scalingFactor});
+  min-width: calc(40px * ${(props) => props.scalingFactor});
+  height: calc(40px * ${(props) => props.scalingFactor});
   margin-right: ${spacing.xs};
 `
 

+ 7 - 1
src/shared/components/VideoPreview/VideoPreviewBase.tsx

@@ -22,6 +22,7 @@ type VideoPreviewBaseProps = {
   metaNode?: React.ReactNode
   onClick?: (e: React.MouseEvent<HTMLElement>) => void
   className?: string
+  scalingFactor?: number
 }
 
 const VideoPreviewBase: React.FC<VideoPreviewBaseProps> = ({
@@ -35,6 +36,7 @@ const VideoPreviewBase: React.FC<VideoPreviewBaseProps> = ({
   metaNode,
   onClick,
   className,
+  scalingFactor = 1,
 }) => {
   const clickable = !!onClick
 
@@ -52,7 +54,11 @@ const VideoPreviewBase: React.FC<VideoPreviewBaseProps> = ({
         <CoverContainer>{coverNode || coverPlaceholder}</CoverContainer>
       </CoverWrapper>
       <InfoContainer main={main}>
-        {displayChannel && <AvatarContainer>{channelAvatarNode || channelAvatarPlaceholder}</AvatarContainer>}
+        {displayChannel && (
+          <AvatarContainer scalingFactor={scalingFactor}>
+            {channelAvatarNode || channelAvatarPlaceholder}
+          </AvatarContainer>
+        )}
         <TextContainer>
           {titleNode || titlePlaceholder}
           {displayChannel && (channelNameNode || channelNamePlaceholder)}