<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
最近寫了一個好玩的 Button,它除了是一個 Button 外,還可以當鏡子照。
那這個好玩的 Button 是怎麼實現的呢?
很容易想到是用到了攝像頭。
沒錯,這裡要使用瀏覽器的獲取媒體裝置的 api 來拿到攝像頭的視訊流,設定到 video 上,然後對 video 做下映象反轉,加點模糊就好了。
button 的部分倒是很容易,主要是陰影稍微麻煩點。
把 video 作為 button 的子元素,加個 overflow:hidden 就完成了上面的效果。
思路很容易,那我們就來實現下吧。
獲取攝像頭用的是 navigator.mediaDevices.getUserMedia 的 api。
在 MDN 中可以看到 mediaDevices 的介紹:
可以用來獲取攝像頭、麥克風、螢幕等。
它有這些 api:
getDisplayMedia 可以用來錄製螢幕,截圖。
getUserMedia 可以獲取攝像頭、麥克風的輸入。
它要指定音訊和視訊的引數,開啟、關閉、解析度、前後攝像頭啥的:
這裡我們把 video 開啟,把 audio 關閉。
也就是這樣:
navigator.mediaDevices.getUserMedia({ video: true, audio: false, }) .then((stream) => { //... }).catch(e => { console.log(e) })
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 可以組合,調整調整達到這樣的效果就可以了:
video { filter: blur(2px) saturate(0.6) brightness(1.1); }
然後調整下大小:
video { width: 300px; height: 100px; filter: blur(2px) saturate(0.6) brightness(1.1); }
你會發現視訊的畫面沒有達到設定的寬高。
這時候通過 object-fit 的樣式來設定:
video { width: 300px; height: 100px; object-fit: cover; filter: blur(2px) saturate(0.6) brightness(1.1); }
cover 是充滿容器,也就是這樣:
但畫面顯示的位置不大對,看不到臉。我想顯示往下一點的畫面怎麼辦呢?
可以通過 object-position 來設定:
video { width: 300px; height: 100px; object-fit: cover; filter: blur(2px) saturate(0.6) brightness(1.1); object-position: 0 -100px; }
y 向下移動 100 px ,也就是這樣的:
現在畫面顯示的位置就對了。
其實現在還有一個特別隱蔽的問題,不知道大家發現沒,就是方向是錯的。照鏡子的時候應該左右翻轉才對。
所以加一個 scaleX(-1),這樣就可以繞 x 周反轉了。
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 部分,這個我們倒是經常寫:
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 狀態,設定不同的 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 都是絕對定位來做的居中。
陰影的 4 個值是 x、y、擴散半徑、顏色。
我設定了個多重陰影:
然後再改成不同透明度的黑就可以了:
再就是按下時的陰影,設定了上下位置的 1px 黑色陰影:
.button.pressed { box-shadow: 0px -1px 1px rgba(0, 0, 0, 0.5), 0px 1px 1px rgba(0, 0, 0, 0.5); }
同時,按下時還有個 scale 的設定:
再就是文字的陰影,也是上下都設定了 1px 陰影,達到環繞的效果:
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; }
瀏覽器提供了 media devices 的 api,可以獲取攝像頭、螢幕、麥克風等的輸入。
除了常規的用途外,還可以用來做一些好玩的事情,比如今天這個的可以照鏡子的 button。
它看起來就像我上廁所時看到的這個東西一樣
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45