JS實現(xiàn)一個可以當鏡子照的?Button
正文
最近寫了一個好玩的 Button,它除了是一個 Button 外,還可以當鏡子照。
那這個好玩的 Button 是怎么實現(xiàn)的呢?
很容易想到是用到了攝像頭。
沒錯,這里要使用瀏覽器的獲取媒體設(shè)備的 api 來拿到攝像頭的視頻流,設(shè)置到 video 上,然后對 video 做下鏡像反轉(zhuǎn),加點模糊就好了。
button 的部分倒是很容易,主要是陰影稍微麻煩點。
把 video 作為 button 的子元素,加個 overflow:hidden 就完成了上面的效果。
思路很容易,那我們就來實現(xiàn)下吧。
獲取攝像頭用的是 navigator.mediaDevices.getUserMedia 的 api。
mediaDevices 的介紹
在 MDN 中可以看到 mediaDevices 的介紹:
可以用來獲取攝像頭、麥克風、屏幕等。
它有這些 api:
getDisplayMedia 可以用來錄制屏幕,截圖。
getUserMedia 可以獲取攝像頭、麥克風的輸入。
我們這里用到getUserMedia 的 api
它要指定音頻和視頻的參數(shù),開啟、關(guān)閉、分辨率、前后攝像頭啥的:
這里我們把 video 開啟,把 audio 關(guān)閉。
也就是這樣:
navigator.mediaDevices.getUserMedia({ video: true, audio: false, }) .then((stream) => { //... }).catch(e => { console.log(e) })
把獲取到的 stream 用一個 video 來展示
navigator.mediaDevices.getUserMedia({ video: true, audio: false, }) .then((stream) => { const video = document.getElementById('video'); video.srcObject = stream; video.onloadedmetadata = () => { video.play(); }; }) .catch((e) => console.log(e));
就是這樣的:
通過 css 的 filter 來加點感覺:
比如加點 blur:
video { filter: blur(10px); }
加點飽和度:
video { filter: saturate(5) }
或者加點亮度:
video: { filter: brightness(3); }
filter 可以組合,調(diào)整調(diào)整達到這樣的效果就可以了:
video { filter: blur(2px) saturate(0.6) brightness(1.1); }
然后調(diào)整下大?。?/p>
video { width: 300px; height: 100px; filter: blur(2px) saturate(0.6) brightness(1.1); }
你會發(fā)現(xiàn)視頻的畫面沒有達到設(shè)置的寬高。
這時候通過 object-fit 的樣式來設(shè)置:
video { width: 300px; height: 100px; object-fit: cover; filter: blur(2px) saturate(0.6) brightness(1.1); }
cover 是充滿容器,也就是這樣:
但畫面顯示的位置不大對,看不到臉。我想顯示往下一點的畫面怎么辦呢?
可以通過 object-position 來設(shè)置:
video { width: 300px; height: 100px; object-fit: cover; filter: blur(2px) saturate(0.6) brightness(1.1); object-position: 0 -100px; }
y 向下移動 100 px ,也就是這樣的:
現(xiàn)在畫面顯示的位置就對了。
其實現(xiàn)在還有一個特別隱蔽的問題,不知道大家發(fā)現(xiàn)沒,就是方向是錯的。照鏡子的時候應該左右翻轉(zhuǎn)才對。
所以加一個 scaleX(-1),這樣就可以繞 x 周反轉(zhuǎn)了。
video { width: 300px; height: 100px; object-fit: cover; filter: blur(2px) saturate(0.6) brightness(1.1); object-position: 0 -100px; transform: scaleX(-1); }
這樣就是鏡面反射的感覺了。
然后再就是 button 部分,這個我們倒是經(jīng)常寫:
function Button({ children }) { const [buttonPressed, setButtonPressed] = useState(false); return ( <div className={`button-wrap ${buttonPressed ? "pressed" : null}`} > <div className={`button ${buttonPressed ? "pressed" : null}`} onPointerDown={() => setButtonPressed(true)} onPointerUp={() => setButtonPressed(false)} > <video/> </div> <div className="text">{children}</div> </div> ); }
這里我用 jsx 寫的,點擊的時候修改 pressed 狀態(tài),設(shè)置不同的 class。
樣式部分
:root { --transition: 0.1s; --border-radius: 56px; } .button-wrap { width: 300px; height: 100px; position: relative; transition: transform var(--transition), box-shadow var(--transition); } .button-wrap.pressed { transform: translateZ(0) scale(0.95); } .button { width: 100%; height: 100%; border: 1px solid #fff; overflow: hidden; border-radius: var(--border-radius); box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.25), 0px 8px 16px rgba(0, 0, 0, 0.15), 0px 16px 32px rgba(0, 0, 0, 0.125); transform: translateZ(0); cursor: pointer; } .button.pressed { box-shadow: 0px -1px 1px rgba(0, 0, 0, 0.5), 0px 1px 1px rgba(0, 0, 0, 0.5); } .text { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); pointer-events: none; color: rgba(0, 0, 0, 0.7); font-size: 48px; font-weight: 500; text-shadow:0px -1px 0px rgba(255, 255, 255, 0.5),0px 1px 0px rgba(255, 255, 255, 0.5); }
這種 button 大家寫的很多了,也就不用過多解釋。
要注意的是 text 和 video 都是絕對定位來做的居中。
陰影的設(shè)置
陰影的 4 個值是 x、y、擴散半徑、顏色。
我設(shè)置了個多重陰影:
然后再改成不同透明度的黑就可以了:
再就是按下時的陰影,設(shè)置了上下位置的 1px 黑色陰影:
.button.pressed { box-shadow: 0px -1px 1px rgba(0, 0, 0, 0.5), 0px 1px 1px rgba(0, 0, 0, 0.5); }
同時,按下時還有個 scale 的設(shè)置:
再就是文字的陰影,也是上下都設(shè)置了 1px 陰影,達到環(huán)繞的效果:
text-shadow:0px -1px 0px rgba(255, 255, 255, 0.5),0px 1px 0px rgba(255, 255, 255, 0.5);
最后,把這個 video 嵌進去就行了。
完整代碼
import React, { useState, useEffect, useRef } from "react"; import "./button.css"; function Button({ children }) { const reflectionRef = useRef(null); const [buttonPressed, setButtonPressed] = useState(false); useEffect(() => { if (!reflectionRef.current) return; navigator.mediaDevices.getUserMedia({ video: true, audio: false, }) .then((stream) => { const video = reflectionRef.current; video.srcObject = stream; video.onloadedmetadata = () => { video.play(); }; }) .catch((e) => console.log(e)); }, [reflectionRef]); return ( <div className={`button-wrap ${buttonPressed ? "pressed" : null}`} > <div className={`button ${buttonPressed ? "pressed" : null}`} onPointerDown={() => setButtonPressed(true)} onPointerUp={() => setButtonPressed(false)} > <video className="button-reflection" ref={reflectionRef} /> </div> <div className="text">{children}</div> </div> ); } export default Button;
body { padding: 200px; } :root { --transition: 0.1s; --border-radius: 56px; } .button-wrap { width: 300px; height: 100px; position: relative; transition: transform var(--transition), box-shadow var(--transition); } .button-wrap.pressed { transform: translateZ(0) scale(0.95); } .button { width: 100%; height: 100%; border: 1px solid #fff; overflow: hidden; border-radius: var(--border-radius); box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.25), 0px 8px 16px rgba(0, 0, 0, 0.15), 0px 16px 32px rgba(0, 0, 0, 0.125); transform: translateZ(0); cursor: pointer; } .button.pressed { box-shadow: 0px -1px 1px rgba(0, 0, 0, 0.5), 0px 1px 1px rgba(0, 0, 0, 0.5); } .text { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); pointer-events: none; color: rgba(0, 0, 0, 0.7); font-size: 48px; font-weight: 500; text-shadow:0px -1px 0px rgba(255, 255, 255, 0.5),0px 1px 0px rgba(255, 255, 255, 0.5); } .text::selection { background-color: transparent; } .button .button-reflection { width: 100%; height: 100%; transform: scaleX(-1); object-fit: cover; opacity: 0.7; filter: blur(2px) saturate(0.6) brightness(1.1); object-position: 0 -100px; }
總結(jié)
瀏覽器提供了 media devices 的 api,可以獲取攝像頭、屏幕、麥克風等的輸入。
除了常規(guī)的用途外,還可以用來做一些好玩的事情,比如今天這個的可以照鏡子的 button。
它看起來就像我上廁所時看到的這個東西一樣??:
以上就是JS實現(xiàn)一個可以當鏡子照的 Button的詳細內(nèi)容,更多關(guān)于JS鏡子Button的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
判斷Spartacus?SSR的Transfer?State是否正常工作技巧
這篇文章主要為大家介紹了判斷Spartacus?SSR的Transfer?State是否正常工作技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10詳解微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示的相關(guān)資料,需要的朋友可以參考下2017-05-05js前端設(shè)計模式優(yōu)化50%表單校驗代碼示例
這篇文章主要為大家介紹了js前端設(shè)計模式優(yōu)化50%表單校驗代碼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06