I have a functional code on my desktop and in all browsers, where each button has a 4-second animation, and on load or hover, each button triggers the loading of different videos. However, on iOS, the animation or video doesn't play automatically. I have to manually touch or hover over a button to initiate the video or animation.
"use client"
import React, { useEffect, useRef, useState } from "react"
import Link from "next/link"
import { HomePageScreen as HomePageScreenType } from "@/types"
import { animated, SpringConfig, useSprings } from "react-spring"
import { IconsLibrary } from "@/components/shared"
const DURATION = 400
const INTERVAL = 4000
const easeOutQuad = (t: number) => t * (2 - t)
const easeInOutCubic = (t: number) =>
t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
type HomePageScreenProps = {
data: HomePageScreenType[]
}
const HomePageScreen = ({ data }: HomePageScreenProps) => {
if (!data) return null
const [loaded, setLoaded] = useState({ videos: false, content: false })
const [activeButton, setActiveButton] = useState<number | null>(null)
useEffect(() => {
const videoLoadListeners = data.map((item, index) => {
const video = document.createElement("video")
video.src = item.caseLink?.case.cloudinary.secure_url ?? ""
video.preload = "auto"
video.oncanplaythrough = () => {
if (index === data.length - 1) {
setLoaded((prev) => ({ ...prev, videos: true }))
}
}
return video
})
const contentLoadTimeout = setTimeout(() => {
setLoaded((prev) => ({ ...prev, content: true }))
}, 0)
return () => {
videoLoadListeners.forEach((video) => {
video.oncanplaythrough = null
video.remove()
})
clearTimeout(contentLoadTimeout)
}
}, [data])
useEffect(() => {
if (loaded.videos && loaded.content) {
setActiveButton(0)
startInterval()
}
return () => clearInterval(intervalRef.current)
}, [loaded.videos, loaded.content])
const videoSprings = useSprings(
data.length,
data.map((item, index) => ({
opacity: activeButton === index ? 1 : 0,
scale: activeButton === index ? 1.1 : 1,
config: {
duration: DURATION,
easing: activeButton === index ? easeInOutCubic : easeOutQuad
} as SpringConfig
}))
)
const intervalRef = useRef<NodeJS.Timeout | undefined>(undefined)
const handleMouseEnter = (index: number) => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current)
}
setActiveButton(index)
}
const handleMouseLeave = () => {
startInterval()
}
const startInterval = () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current)
}
intervalRef.current = setInterval(() => {
setActiveButton((prev) => (prev === null ? 0 : (prev + 1) % data.length))
}, INTERVAL)
}
return (
<section className="col-span-full overflow-hidden text-white bg-black absolute top-0 left-0 z-0 w-full h-full px-5 md:px-10 flex flex-col items-center justify-between">
<div className="absolute bottom-36 left-[30px] z-[400] flex w-full flex-wrap gap-[15px] md:bottom-20 max-w-[90%] md:left-[40px] md:max-w-[90%] xl:left-[150px] lg:max-w-[920px]">
<h1 className="hero z-[10] font-micro-medium text-[38px] leading-tight text-white md:text-headingL inline-block">
{data.map((item, index) => (
<span key={item.caseLink?.case.slug.current}>
<span className="mr-2 md:mr-4">{item.textBlock}</span>
<div className="inline-flex">
<Link
className="emoji eyes text-inherit text-lg md:text-2xl -top-2 mr-2 md:mr-4 overflow-hidden px-[1.25rem] py-[0.297rem] md:py-[0.625rem] border-2 border-white rounded-[100px] relative transition-all duration-300 ease-in-out hover:bg-white hover:text-black group"
href={`/cases/${item.caseLink?.case.slug.current}`}
onMouseEnter={() => handleMouseEnter(index)}
onMouseLeave={handleMouseLeave}
>
<span
className={`mix-blend-difference z-[100] relative group-hover:mix-blend-normal text-current`}
>
{item.caseLink?.case.title}
</span>
<span
className={`h-full absolute z-10 top-0 left-0 transition-all duration-500 bg-white ${
activeButton === index ? "animate-progress" : "opacity-0 w-0"
}`}
></span>
</Link>
</div>
</span>
))}
<Link
href="/cases"
className="group emoji eyes inline-flex -top-2 h-[36px] md:h-[54px] sm:-ml-1 place-content-center items-center w-[70px] md:w-[90px] overflow-hidden px-[1.25rem] py-[0.625rem] border-2 border-white/50 rounded-[100px] relative transition-all duration-300 ease-in-out hover:bg-white"
>
<IconsLibrary
type="arrow-right"
className="h-[12px] w-[14px] md:h-[16px] md:w-[18px] fill-white group-hover:fill-black transition-all duration-300 ease-in-out"
/>
</Link>
</h1>
</div>
{data.map((item, index) => {
const originalUrl = item.caseLink?.case.cloudinary.secure_url
return (
<animated.video
src={originalUrl}
autoPlay
loop
key={index}
muted
playsInline
className="absolute w-screen h-screen top-0 left-0 object-cover"
style={{
...videoSprings[index],
transformOrigin: "center center",
transform: videoSprings[index].scale?.to((s) => `scale(${s})`),
opacity: videoSprings[index].opacity?.to((o) => o.toFixed(2))
}}
/>
)
})}
<div className="fixed top-0 left-0 w-screen h-screen bg-black/40 z-10"></div>
</section>
)
}
export default HomePageScreen
Figured it out. Thanks low power mode and removing
video.oncanplaythrough = () =>
solved the problem.