JS實現(xiàn)一個可以當(dāng)鏡子照的?Button
正文
最近寫了一個好玩的 Button,它除了是一個 Button 外,還可以當(dāng)鏡子照。

那這個好玩的 Button 是怎么實現(xiàn)的呢?
很容易想到是用到了攝像頭。
沒錯,這里要使用瀏覽器的獲取媒體設(shè)備的 api 來拿到攝像頭的視頻流,設(shè)置到 video 上,然后對 video 做下鏡像反轉(zhuǎn),加點模糊就好了。
button 的部分倒是很容易,主要是陰影稍微麻煩點。
把 video 作為 button 的子元素,加個 overflow:hidden 就完成了上面的效果。
思路很容易,那我們就來實現(xiàn)下吧。
獲取攝像頭用的是 navigator.mediaDevices.getUserMedia 的 api。
mediaDevices 的介紹
在 MDN 中可以看到 mediaDevices 的介紹:

可以用來獲取攝像頭、麥克風(fēng)、屏幕等。
它有這些 api:

getDisplayMedia 可以用來錄制屏幕,截圖。
getUserMedia 可以獲取攝像頭、麥克風(fēng)的輸入。

我們這里用到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)沒,就是方向是錯的。照鏡子的時候應(yīng)該左右翻轉(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,可以獲取攝像頭、屏幕、麥克風(fēng)等的輸入。
除了常規(guī)的用途外,還可以用來做一些好玩的事情,比如今天這個的可以照鏡子的 button。
它看起來就像我上廁所時看到的這個東西一樣??:

以上就是JS實現(xiàn)一個可以當(dāng)鏡子照的 Button的詳細內(nèi)容,更多關(guān)于JS鏡子Button的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
瀏覽器切換到其他標(biāo)簽頁或最小化js定時器是否準(zhǔn)時測試
這篇文章主要為大家介紹了瀏覽器切換到其他標(biāo)簽頁或最小化是js定時器是否準(zhǔn)時的測試詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07
判斷Spartacus?SSR的Transfer?State是否正常工作技巧
這篇文章主要為大家介紹了判斷Spartacus?SSR的Transfer?State是否正常工作技巧,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10
詳解微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過控制CSS實現(xiàn)view隱藏與顯示的相關(guān)資料,需要的朋友可以參考下2017-05-05
js前端設(shè)計模式優(yōu)化50%表單校驗代碼示例
這篇文章主要為大家介紹了js前端設(shè)計模式優(yōu)化50%表單校驗代碼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06

