This article is also available in:
English.
一个简单的 Live Photo 组件实现。
Github: https://github.com/LynanBreeze/live-photo
预览
我的 Live Photo
html代码1
| <iframe src="/static/live-photo/?picUrl=https://r2-assets.thelynan.com/u/A001_07251216_C317-FfGABM.jpg&videoUrl=https://r2-assets.thelynan.com/u/A001_07251216_C317-DQ6Nj6.mp4" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 100%; aspect-ratio: 16/9;"></iframe>
|
Apple 官方 Live Photo
html代码1
| <iframe src="/static/live-photo/?picUrl=/static/live-photo/test/live.jpg&videoUrl=/static/live-photo/test/live.mp4&useApple=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true" style="width: 100%; aspect-ratio: 16/9;"></iframe>
|
对比
我的 |
Apple |
☑️ 更好的兼容性 |
在 iOS 微信中异常 |
☑️ 声音、静音可选 |
静音 |
|
☑️ 更丰富的播放器效果选项 |
点击触发 |
点击触发(桌面端), 长按触发(移动端) |
个性化的图标 |
和相册里的一样 |
参数
参数名称 |
类型 |
photoSrc |
string, 必须 |
videoSrc |
string, 必须 |
loop |
boolean |
muted |
boolean |
useApple |
boolean |
原理
Live Photo并不是一种神秘的文件格式,而是一张照片和一段视频的组合。
所以需要展示 Live Photo 只需添加一个切换逻辑即可。
Apple SDK
Apple 为开发者提供了官方的 JS SDK。请查看 https://developer.apple.com/documentation/livephotoskitjs
index.html1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cdn.apple-livephotoskit.com/lpk/1/livephotoskit.js"></script> </head> <body> <div data-live-photo data-photo-src="https://..." data-video-src="https://..." style="width: 320px; height: 320px"> </div> </body> </html>
|
如果要在React中实现,很简单,如下:
index.jsx1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { useRef, useEffect } from "react"; import * as LivePhotosKit from "livephotoskit";
const LivePhotosKitReact = ({ className, photoSrc, videoSrc }) => { const nodeRef = useRef(null);
useEffect(() => { const player = LivePhotosKit.Player(nodeRef.current); player.photoSrc = photoSrc; player.videoSrc = videoSrc; }, []);
return <div ref={nodeRef} className={className}></div>; };
|
My Style
我们所需要的只是在 <img>
和 <video>
之间切换。
index.jsx1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| import { useRef, useState } from "react";
const LivePhoto = (props) => { const { photoSrc, videoSrc, muted, loop, useApple } = props; const [imageReady, setImageReady] = useState(false); const [videoPlaying, setVideoPlaying] = useState(false); const [videoRunning, setVideoRunning] = useState(false); const [videoReady, setVideoReady] = useState(false); const videoRef = useRef(null);
const playVideo = () => { if (videoRunning) { videoRef.current.pause(); } else { setVideoPlaying(true); videoRef.current.play(); } };
const onImageLoad = () => { setImageReady(true); if ( /iphone/i.test(navigator.userAgent) && /micromessenger/i.test(navigator.userAgent) ) { setTimeout(() => { setVideoReady(true); }, 500); } };
return ( <div className='live-photo'> {useApple ? ( <LivePhotosKitReact className='live-img' photoSrc={photoSrc} videoSrc={videoSrc} /> ) : ( <> <div className='live-trigger' onClick={playVideo} style={{ opacity: Number(videoReady) }} > <div className='trigger-icon' style={{ animationPlayState: videoRunning ? "running" : "paused", }} ></div> <span className='trigger-text'>LIVE</span> </div> <img className='live-img' src={photoSrc} onLoad={onImageLoad} style={{ opacity: Number(imageReady) }} /> <video playsInline webkit-playsinline className='live-video' loop={loop} muted={muted} ref={videoRef} src={videoSrc} style={{ opacity: Number(videoPlaying) }} onCanPlay={() => setVideoReady(true)} onLoadedMetadata={() => setVideoReady(true)} onPlaying={() => setVideoRunning(true)} onPause={() => setVideoRunning(false)} onEnded={() => setVideoPlaying(false)} ></video> </> )} </div> ); };
|
开发 && 构建
开发
1
| pnpm install && pnpm run dev
|
构建