概要
ぼやけた背景に、マウスをあてたところだけ焦点の合う背景を実装します。
本記事は、以下の動画をReactで実装したものです。
スタイリングには、emotion/css(CSS in JS)を使用しています。
実装
実装のイメージ図です。
画像を2枚重ねて、下の画像はblurをかけ、上の画像はclip-pathを使用して一部だけ見えるようにします。
.tsx
import React, { useRef, VFC } from 'react';
import { css } from '@emotion/css';
export const FocusBlur: VFC = () => {
const containerRef = useRef<HTMLDivElement>(null)
const mousemoveHandler = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
containerRef.current!.style.setProperty('--x', e.clientX + 'px')
containerRef.current!.style.setProperty('--y', e.clientY + 'px')
}
return (
<div ref={containerRef} className={styles.container} onMouseMove={mousemoveHandler}>
<div className={styles.box}>
<h2 className={styles.text}>Forcus</h2>
</div>
<div className={styles.box}>
<h2 className={styles.text}>Forcus</h2>
</div>
<div className={styles.circle}></div>
</div>
)
}
const templates = {
flex: css`
display: flex;
justify-content: center;
align-items: center;
`,
image: css`
background-image: url('/assets/bg.jpg');
background-size: cover;
background-position: center;
background-attachment: fixed;
`
}
const styles = {
container: css`
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
${templates.flex}
${templates.image}
`,
box: css`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
${templates.flex}
${templates.image}
&:nth-child(1) {
filter: blur(10px);
}
&:nth-child(2) {
clip-path: circle(150px at var(--x) var(--y));
}
`,
text: css`
position: absolute;
color: #fff;
font-size: 10rem;
user-select: none;
transform: translate(calc(var(--x) / 25), calc(var(--y) / 25));
`,
circle: css`
position: absolute;
top: -150px;
left: -150px;
width: 300px;
height: 300px;
border: 2px solid #fff;
border-radius: 50%;
box-shadow: 0px 5px 25px rgba(0, 0, 0, 0.25);
transform: translate(var(--x), var(--y));
`
}
-
templatesで、共通に使えるCSSをまとめています。
-
setPropertyを使用して、マウス位置をCSS変数
--x
・--y
に格納しています。
CSS変数のスコープを、このコンポーネントだけにしたいので、containerRefに割り当てています。
.tsx
const mousemoveHandler = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
containerRef.current!.style.setProperty('--x', e.clientX + 'px')
containerRef.current!.style.setProperty('--y', e.clientY + 'px')
}