0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

背景画像をコンテンツの高さにあわせて追従させる処理を考えてみた

0
Posted at

この記事について

備忘録。各サンプルファイルを残しておく。

html

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>背景追従</title>
    <link rel="stylesheet" href="./assets/css/style.css">
    <script src="./assets/js/script.js" defer></script>
</head>

<body>
    <header>
        <p>ヘッダーです</p>
    </header>
    <main>
        <div class="announcement-area">
            <p>お知らせエリアです</p>
        </div>

        <section class="scene" id="js-scene">
            <div class="scene__background" id="js-background">
                <img src="./assets/bg_pc.JPG" alt="">
            </div>

            <div class="scene__content" id="js-content">
                <h2>コンテンツタイトル</h2>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
                <section>
                    <p>高さが変化するコンテンツです。</p>
                </section>
            </div>
        </section>
    </main>
    <footer>
        <p>footerです</p>
    </footer>
</body>

</html>

css

@charset "utf-8";

html,
body,
*::before,
*::after {
    box-sizing: border-box;
    padding: 0;
    margin: 0;
}

html {
    font-family: sans-serif;
    font-size: 62.5%;
    color: #333;
    font-weight: 400;
    line-height: 1.2;
}

body {
    font-size: 1.6rem;
}

header {
    background: #fff;
    height: 60px;
    width: 100%;
    border-bottom: 1px solid #333;
    display: flex;
    align-items: center;
}

footer {
    background: #fff;
    height: 100px;
    width: 100%;
    border-top: 1px solid #333;
    display: flex;
    align-items: center;

}

header p,
footer p,
.announcement-area p,
.content-area p {
    width: fit-content;
    margin: auto;
    font-weight: 700;
    font-size: 2.6rem;

}


* {
    box-sizing: border-box;
}

body {
    margin: 0;
}


.scene__content section {
    height: 200px;
}


/* ここから ============================== */

html,
body {
  overflow-x: clip;
}

.scene {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  isolation: isolate;
}

.scene__background,
.scene__content {
  grid-area: 1 / 1;
  align-self: start;
  min-width: 0;
}

.scene__background {
  position: relative;
  z-index: 0;
  overflow: visible;
}

/* 背景画像を一時固定 */
.scene__background.is-sticky {
  position: sticky;
  top: var(--sticky-top, 0px);
}

/* コンテンツが画像より短い場合 */
.scene__background.is-cropped {
  overflow: hidden;
}

.scene__background img {
  display: block;
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);

  width: max(100%, 1366px);
  max-width: none;
  height: auto;
}

.scene__content {
  position: relative;
  z-index: 1;
  padding: 80px 5%;
}

footer {
  position: relative;
  z-index: 2;
}

/* ここまで ============================== */

js


'use strict';

const scene = document.getElementById('js-scene');
const background = document.getElementById('js-background');
const content = document.getElementById('js-content');
const image = background.querySelector('img');

// 要素の寸法とstickyの位置を計算
const measure = () => {
  if (!image.naturalWidth || !image.naturalHeight) {
    return;
  }

  // 実際の表示高さを取得
  const imageHeight = image.getBoundingClientRect().height;
  const contentHeight = content.getBoundingClientRect().height;

  if (!imageHeight) return;

  // コンテンツと画像のうち小さい方を表示領域にする
  const displayHeight = Math.min(
    imageHeight,
    contentHeight
  );

  // 背景画像の表示領域を確保
  const currentHeight =
    background.getBoundingClientRect().height;

  if (Math.abs(currentHeight - displayHeight) > 0.5) {
    background.style.height = `${displayHeight}px`;
  }

  // コンテンツが画像より短い場合はトリミング
  const shouldCrop = contentHeight < imageHeight;

  background.classList.toggle(
    'is-cropped',
    shouldCrop
  );

  // コンテンツが画像より長い場合だけstickyを有効化
  const shouldStick = contentHeight > imageHeight;

  background.classList.toggle(
    'is-sticky',
    shouldStick
  );

  if (shouldStick) {
    // セクションの文書内開始位置
    const sceneTop =
      scene.getBoundingClientRect().top + window.scrollY;

    // 背景画像の下端がビューポートに入った位置
    const stickyTop = Math.min(
      sceneTop,
      window.innerHeight - imageHeight
    );

    background.style.setProperty(
      '--sticky-top',
      `${stickyTop}px`
    );
  } else {
    background.style.removeProperty('--sticky-top');
  }
};

// サイズの変化を監視
const resizeObserver = new ResizeObserver(measure);

resizeObserver.observe(image);
resizeObserver.observe(content);

// ビューポートのサイズ変更
window.addEventListener('resize', measure);

// 画像読み込み後に初期化
if (image.complete && image.naturalWidth) {
  measure();
} else {
  image.addEventListener('load', measure, {
    once: true
  });
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?