Skip to content

Instantly share code, notes, and snippets.

@mlshv
Last active January 14, 2025 06:15
Show Gist options
  • Save mlshv/a3d922cc42509d3670f0f3a27f2b1afe to your computer and use it in GitHub Desktop.
Save mlshv/a3d922cc42509d3670f0f3a27f2b1afe to your computer and use it in GitHub Desktop.
Framer Video Component. Fixes iOS issues
<script>
function playVideos() {
var videoElements = document.getElementsByTagName("video");
for (const video of videoElements) {
const isVideoPlaying = (video) =>
!!(
video.currentTime > 0 &&
!video.paused &&
!video.ended &&
video.readyState > 2
);
if (!isVideoPlaying(video)) {
video.play();
}
}
}
// bypass ios safari not playing autoplay videos in power saving mode
document.body.addEventListener("touchstart", playVideos);
document.body.addEventListener("click", playVideos);
</script>
// Welcome to Code in Framer
// Get Started: https://www.framer.com/developers
import { useEffect, useState } from "react"
import { addPropertyControls, ControlType } from "framer"
import { motion } from "framer-motion"
const isSafari = () => {
const ua = navigator.userAgent.toLowerCase()
return ua.includes("safari") && !ua.includes("chrome")
}
/**
* These annotations control how your component sizes
* Learn more: https://www.framer.com/developers/#code-components-auto-sizing
*
* @framerSupportedLayoutWidth fixed
* @framerSupportedLayoutHeight fixed
*/
export default function Video({
source,
url,
upload,
posterShouldUseUrl,
posterUrl,
posterUpload,
width,
height,
minHeight,
minWidth,
controls,
background,
value,
...props
}) {
const poster = posterShouldUseUrl ? posterUrl : posterUpload
const src = source ? url : upload
const [isLoaded, setIsLoaded] = useState(false)
const [loadedVideoSrc, setLoadedVideoSrc] = useState(null)
useEffect(() => {
const preloadVideo = async (url) => {
try {
const response = await fetch(url, { priority: "high" })
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const videoBlob = await response.blob()
const videoObjectURL = URL.createObjectURL(videoBlob)
setIsLoaded(true)
setLoadedVideoSrc(videoObjectURL)
} catch (error) {
console.error("Error preloading video:", error)
}
}
preloadVideo(src)
}, [src])
return (
<>
{!isLoaded && (
<img
src={poster}
alt=""
style={{
width,
height,
background,
maxHeight: "100%",
maxWidth: "100%",
minHeight,
minWidth,
}}
fetchpriority="high"
/>
)}
{isLoaded && (
<video
{...props}
autoPlay
playsInline
src={loadedVideoSrc}
style={{
...props.style,
position: "absolute",
top: 0,
left: 0,
maxWidth: "100%",
maxHeight: "100%",
background,
minHeight,
minWidth,
}}
poster={poster}
controls={controls}
/>
)}
</>
)
}
Video.defaultProps = {
url: "https://joinhuman-com-statics.s3.amazonaws.com/joinhuman-com-videos/optimized/id-white.mp4",
upload: "https://joinhuman-com-statics.s3.amazonaws.com/joinhuman-com-videos/optimized/network.mp4",
posterUrl:
"https://joinhuman-com-statics.s3.amazonaws.com/joinhuman-com-videos/posters/human-network.jpg",
minHeight: 100,
minWidth: 100,
}
addPropertyControls(Video, {
source: {
type: ControlType.Boolean,
title: "Source",
enabledTitle: "URL",
disabledTitle: "Upload",
},
url: {
type: ControlType.String,
title: "URL",
hidden(props) {
return props.source === false
},
description:
"Hosted video file URL. For Youtube, use the Youtube component.",
},
upload: {
type: ControlType.File,
title: "Upload",
hidden(props) {
return props.source === true
},
allowedFileTypes: ["mp4", "mov", "webm"],
},
posterShouldUseUrl: {
type: ControlType.Boolean,
title: "Poster",
enabledTitle: "URL",
disabledTitle: "Upload",
},
posterUrl: {
type: ControlType.String,
title: "URL",
hidden(props) {
return props.posterShouldUseUrl === false
},
},
posterUpload: {
type: ControlType.File,
title: "Upload",
hidden(props) {
return props.posterShouldUseUrl === true
},
allowedFileTypes: ["jpg", "jpeg", "png"],
},
background: {
title: "Background",
type: ControlType.Color,
defaultValue: "#00000000",
},
loop: {
title: "Loop",
type: ControlType.Boolean,
enabledTitle: "Yes",
disabledTitle: "No",
},
controls: {
title: "Controls",
type: ControlType.Boolean,
enabledTitle: "Show",
disabledTitle: "Hide",
defaultValue: false,
},
muted: {
title: "Muted",
type: ControlType.Boolean,
enabledTitle: "Yes",
disabledTitle: "No",
defaultValue: true,
},
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment