真ん中をくり抜くギミック(スクロール連動)

CSS

プロパティ

// マスク画像を2つ用意します
mask-image: url('mask1.png'), url('mask2.png');

// それぞれの画像のサイズを指定します(1つ目の画像サイズは100px 100px)
mask-size: 100px 100px, 50px 50px;

// 1つ目の画像は左上に、2つ目の画像は左上から50px 50pxの位置に配置します
mask-position: 0 0, 50px 50px

// 画像を繰り返しません
mask-repeat: no-repeat;

// 2つのマスク画像を合成します(add, exclude, intersect, subtract)
mask-composite: exclude

これはマスク画像が3枚の場合も同様です

真ん中をくり抜くギミック

<div></div>

<style>
div {
    mask-image: linear-gradient(#000, #000), url("cirle.svg") ※1
    mask-repeat: no-repeat;
    mask-position: 0 0, center center;
    mask-size: 100% 100%, 480px 320px;
    mask-composite: exclude;
}
</style>

※1 … linear-gradient(#000, #000)で領域全体にマスクをかけます

これをスクロールと連動させると…

<div class="mask_area">
    <div class="mask_area_inner">
        <div class="wall">
            <div class="wall_mask"></div>
            <div class="text">GO</div>
        </div>
        <img class="wall_image">
    </div>
</div>
.mask_area {
    position: relative;
    width: 640px;
    height: 480px;
    overflow: hidden auto;
    overscroll-behavior-y: none;
    scrollbar-gutter: stable; ※4 
}
.mask_area_inner {
    height: 1600px;
    width: 100%;
}
.wall {
    position: sticky;
    top: 0;
    height: 480px;
    width: 640px;
    overflow: hidden;
}
.wall_mask {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;

    background-color: var(--);
    mask-image: linear-gradient(#000, #000), radial-gradient(#000 0, #000 50%, transparent 50%, transparent 100%); ※1
    mask-position: 0 0, 323px 237px; ※2
    mask-repeat: no-repeat;
    mask-size: 100% 100%, 16px 18px;
    mask-composite: exclude;
    transform-origin: 331px 246px; ※3
}
.text {
    position: absolute;
    top: 225px;
    left: 308px;
    
    font-size: 22px;
    font-weight: 700;
    -webkit-font-smoothing: antialiased;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    transform-origin: 24.5px 18px;
    
    z-index: 1;
}
.wall_image {
    position: sticky;
    top: 0;
    left: 0;
    
    display: block;
    width: 640px;
    height: 480px;
    aspect-ratio: 4/3;
    object-fit: cover;

    margin-top: -480px;
    z-index: -1;
}

※1 … radial-gradientは中心から外側へ向かうグラデーション

中心(0%)は#000で始まり、50%まで#000で塗り続け、50%から100%までtransparentになります

※2 … GOの穴に合う位置にもっていきます

※3 … 要素の変形(回転、拡大縮小など)の基準点を(X軸、Y軸)で指定します

拡大したときに「O」の穴の中心が起点となるような位置にします

※4 … スクロールバーはコンテンツの幅を占有するため、スクロールバーの有無によってコンテンツのレイアウトがわずかに変化することがあります。scroll-gutter:stable とすることで、スクロールバーが表示されているときもそうでないときも、常にスクロールバーの幅分のスペースが確保されて、レイアウトが安定します。

const ENOUGH_SCALE = 94;
const mask_area = document.querySelector(".mask_area");
const wall_mask = document.querySelector(".wall_mask");
const text = document.querySelector(".text");

mask_area.addEventListener("scroll", (event) => {
    // スクロール量に応じた拡大率
    const scaleRatio = 1 + event.target.scrollTop / 11;
    if (event.target.scrollTop < 0) { return; }
    text.style.scale = `${scaleRatio}`;
    wall_mask.style.scale = `${scaleRatio}`;

    // 十分拡大したら非表示にする
    if (scaleRatio > ENOUGH_SCALE) {
        wall_mask.style.visibility = "hidden";
    } else {
        wall_mask.style.visibility = "visible";
    }
});
BACK