Skip to content

Commit

Permalink
Merge pull request #48 from mnsinri/dev
Browse files Browse the repository at this point in the history
feat: ホバー時にmarquee scrollを減速させる
  • Loading branch information
mnsinri authored Sep 4, 2023
2 parents d007f36 + 7613acb commit 4f84e10
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 111 deletions.
16 changes: 13 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-icons": "^4.9.0",
"react-merge-refs": "^2.0.2",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
Expand Down
9 changes: 5 additions & 4 deletions src/components/card/StreamingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,28 @@ export const StreamingCard = React.memo<StreamingCardProps>(
const { isMobile, isDesktopSize } = useWindowSize();
const { config } = useConfig();

const isHoverable = !isMobile && isDesktopSize;
const expand = isMobile || !isDesktopSize;

return (
<Container>
<Card
onClick={() => window.open(url)}
aria-label={title}
{...(isHoverable ? hoverSpread : {})}
{...hoverSpread}
>
<ServiceIcon
service={service}
startAt={startAt}
isExpand={config.isExpandAlways || !isHoverable || hovered}
isExpand={config.isExpandAlways || expand || hovered}
style={{ position: "absolute", top: 5, right: 5, zIndex: 10 }}
/>
<ThumbnailBlock
title={title}
thumbnail={thumbnail}
name={name}
icon={icon}
isExpand={config.isExpandAlways || !isHoverable || hovered}
isExpand={config.isExpandAlways || expand || hovered}
hovered={hovered}
/>
</Card>
</Container>
Expand Down
23 changes: 14 additions & 9 deletions src/components/card/ThumbnailBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import React, { useMemo } from "react";
import styled from "styled-components";
import { animated, easings, useSpring } from "@react-spring/web";
import { breakpoints } from "../../configs";
import { useConfig, useWindowSize } from "../../hooks";
import { Marquee } from "../marquee";
import { MarqueeScroll } from "../marquee";
import { ThumbnailBlockProps } from "../../types";

const Panel = styled(animated.div)`
Expand Down Expand Up @@ -74,7 +74,7 @@ const Contents = styled(animated.div)`
`}
`;

const MarqueeTitle = styled(Marquee)`
const MarqueeTitle = styled(MarqueeScroll)`
font-family: "Zen Kaku Gothic New", sans-serif;
font-size: 10px;
width: 100%;
Expand Down Expand Up @@ -109,6 +109,7 @@ export const ThumbnailBlock: React.FC<ThumbnailBlockProps> = ({
name,
icon,
isExpand,
hovered,
...props
}) => {
const { isPhoneSize } = useWindowSize();
Expand Down Expand Up @@ -139,14 +140,16 @@ export const ThumbnailBlock: React.FC<ThumbnailBlockProps> = ({
...(isPhoneSize ? mobileSpringConfig : {}),
});

const { opacity } = useSpring({
opacity: isExpand ? 1 : 0,
const { x } = useSpring({
x: isExpand ? 1 : 0,
config: {
duration: 250,
duration: 500,
easing: easings.easeOutExpo,
},
});

const speed = useMemo(() => (isPhoneSize ? 0.45 : 0.9), [isPhoneSize]);

return (
<Panel style={{ height }} {...props}>
<Thumbnail
Expand All @@ -157,10 +160,12 @@ export const ThumbnailBlock: React.FC<ThumbnailBlockProps> = ({
/>
<Header>
<Icon src={icon} alt={name} style={{ filter: shadow }} loading="lazy" />
<Contents style={{ opacity }}>
<Contents
style={{ opacity: x.to({ range: [0, 0.75, 1], output: [0, 0, 1] }) }}
>
<MarqueeTitle
animate={isExpand && config.isMarquee}
speed={isPhoneSize ? 0.03 : 0.05}
isAnimate={config.isMarquee && isExpand}
speed={config.isExpandAlways && hovered ? speed / 2 : speed}
>
{title}
</MarqueeTitle>
Expand Down
92 changes: 0 additions & 92 deletions src/components/marquee/Marquee.tsx

This file was deleted.

58 changes: 58 additions & 0 deletions src/components/marquee/MarqueeItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { ReactNode, forwardRef, useEffect, useRef } from "react";
import styled from "styled-components";
import { useAnimationFrame, useWindowSize } from "../../hooks";
import { mergeRefs } from "react-merge-refs";

const Container = styled.div`
white-space: nowrap;
padding: 0 20% 0 3%;
`;

type Props = {
children: ReactNode;
isAnimate: boolean;
speed?: number;
waitTime?: number;
};

export const MarqueeItem = forwardRef<HTMLDivElement, Props>(
({ children, isAnimate, speed = 1, waitTime = 1500 }, forwardedRef) => {
const { isPhoneSize } = useWindowSize();
const item = useRef<HTMLDivElement>(null!);
const rect = useRef<DOMRect>(null!);
const start = useRef<number | null>(null);
const x = useRef<number>(0);

useEffect(() => {
rect.current = item.current.getBoundingClientRect();
}, [children, isPhoneSize]);

useEffect(() => {
item.current.style.transform = `translateX(0)`;
x.current = 0;
start.current = null;
}, [isAnimate]);

useAnimationFrame((timestamp) => {
if (!isAnimate || !item.current || !rect.current) return;

if (!start.current) start.current = timestamp;

if (timestamp - start.current < waitTime) return;

x.current -= speed;
if (x.current < -rect.current.width) {
x.current = 0;
start.current = null;
}

item.current.style.transform = `translateX(${
(x.current / rect.current.width) * 100
}%)`;
});

return (
<Container ref={mergeRefs([item, forwardedRef])}>{children}</Container>
);
}
);
59 changes: 59 additions & 0 deletions src/components/marquee/MarqueeScroll.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { ReactNode, useLayoutEffect, useRef, useState } from "react";
import { MarqueeItem } from "./MarqueeItem";
import styled from "styled-components";
import { useWindowSize } from "../../hooks";

const Container = styled.div`
width: 100%;
display: flex;
overflow: hidden;
mask-image: linear-gradient(
to right,
transparent,
#fff 5%,
#fff 95%,
transparent
);
`;

type Props = {
children: ReactNode;
isAnimate: boolean;
speed?: number;
};

export const MarqueeScroll: React.FC<Props> = ({
children,
isAnimate,
speed = 1,
...props
}) => {
const { isPhoneSize } = useWindowSize();
const parentRef = useRef<HTMLDivElement>(null!);
const childRef = useRef<HTMLDivElement>(null!);
const [canMarquee, setCanMarquee] = useState<boolean>(false);

useLayoutEffect(() => {
setCanMarquee(
parentRef.current.getBoundingClientRect().width <
childRef.current.getBoundingClientRect().width
);
}, [children, isPhoneSize]);

return (
<Container ref={parentRef} {...props}>
<MarqueeItem
ref={childRef}
isAnimate={canMarquee && isAnimate}
speed={speed}
>
{children}
</MarqueeItem>
{canMarquee && (
<MarqueeItem isAnimate={isAnimate} speed={speed}>
{children}
</MarqueeItem>
)}
</Container>
);
};
2 changes: 1 addition & 1 deletion src/components/marquee/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { Marquee } from "./Marquee";
export { MarqueeScroll } from "./MarqueeScroll";
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { useStreamInfo } from "./useStreamInfo";
export { useWindowSize } from "./useWindowSize";
export { useConfig } from "./useConfig";
export { useBoolStateCache } from "./useBoolStateCache";
export { useAnimationFrame } from "./useAnimationFrame";
20 changes: 20 additions & 0 deletions src/hooks/useAnimationFrame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback, useEffect, useRef } from "react";

export const useAnimationFrame = (
callback = (timestamp: DOMHighResTimeStamp) => {}
) => {
const ref = useRef<number>(0);

const loop = useCallback(
(timestamp: DOMHighResTimeStamp) => {
ref.current = requestAnimationFrame(loop);
callback(timestamp);
},
[callback]
);

useEffect(() => {
ref.current = requestAnimationFrame(loop);
return () => cancelAnimationFrame(ref.current);
}, [loop]);
};
5 changes: 4 additions & 1 deletion src/hooks/useHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ export const useHover = () => {
return {
hovered,
hoverSpread: {
onPointerOver: (e: any) => (e.stopPropagation(), setHover(true)),
onPointerOver: (e: any) => {
e.stopPropagation();
setHover(true);
},
onPointerOut: () => setHover(false),
},
};
Expand Down
Loading

0 comments on commit 4f84e10

Please sign in to comment.