記事中に常に表示される「お問い合わせ」などの固定フローティングボタン。便利な一方で、競合他社紹介エリアなど特定セクションでは非表示にしたいケースもあります。本記事では、記事内の特定要素(id="huwastart"
〜 id="huwaend"
)の間にスクロール位置が入ったときだけ、自社ボタンを“ふわふわ”と上下に動かしながら表示/非表示を切り替える実装手法をご紹介します。
課題と要件整理
- 常時固定表示
通常は画面下部にposition: fixed
されたボタンを表示しておく。 - 競合エリアではオフ
記事中に「他社紹介エリア」を設置し、その範囲では自社ボタンを非表示(もしくは別挙動)にしたい。 - 動的アニメーション
競合エリアに入った瞬間から“ふわふわ”浮遊アニメーションを開始。エリア外に出たらアニメーション停止して元位置にリセット。 - デバッグログ
挙動確認のため、スクロール位置や要素検出状況をコンソールログに出力。
HTML/CSS 構造イメージ
<!-- 固定ボタン -->
<div class="column_detail_fix_btn_wrap">
<!-- 内部構造省略 -->
</div>
<!-- 記事中の競合紹介エリア -->
<div id="huwastart"></div>
<!-- ここに他社情報が並ぶ -->
<div id="huwaend"></div>
.column_detail_fix_btn_wrap {
position: fixed;
z-index: 50;
left: 0;
bottom: 0;
width: 100%;
background-color: rgba(255, 255, 255, 0);
padding: 0 0 8px;
/* transform は JS で制御 */
}
JavaScript 実装のポイント
- DOMContentLoaded で確実に DOM を取得
getElementPosition()
で要素の絶対位置(ドキュメントトップからのオフセット)を計算window.addEventListener('scroll', …)
でスクロールごとに位置判定- 範囲内判定後、
requestAnimationFrame
によるサイン波ベースのアニメーション - ログ出力で各種パラメータ(スクロール位置/要素位置/アニメーション状態)を可視化
コード全文
<script>
(function() {
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM読み込み完了 - ふわふわアニメーション初期化開始');
const floatingBtn = document.querySelector('.column_detail_fix_btn_wrap');
const startMarker = document.getElementById('huwastart');
const endMarker = document.getElementById('huwaend');
console.log('要素検出状況:', {
floatingBtn: !!floatingBtn,
startMarker: !!startMarker,
endMarker: !!endMarker
});
if (!floatingBtn || !startMarker || !endMarker) {
console.error('必要な要素が見つかりません。処理を中止します。');
return;
}
function getElementPosition(el) {
let pos = 0;
while (el) {
pos += el.offsetTop - el.scrollTop + el.clientTop;
el = el.offsetParent;
}
return pos;
}
let isAnimating = false, animationId = null, time = 0;
const speed = 0.005;
function checkScrollPosition() {
const scrollY = window.scrollY || window.pageYOffset;
const startY = getElementPosition(startMarker);
const endY = getElementPosition(endMarker);
const inArea = scrollY >= startY && scrollY <= endY;
console.log('スクロール位置チェック:', {scrollY, startY, endY, inArea});
if (inArea && !isAnimating) {
startAnimation();
} else if (!inArea && isAnimating) {
stopAnimation();
}
}
function startAnimation() {
isAnimating = true;
time = 0;
console.log('アニメーション開始');
function animate() {
time += speed;
const sine = Math.sin(time);
const vh = window.innerHeight;
const maxPixels = vh * 0.3;
const yOffset = (sine + 1) / 2 * maxPixels;
floatingBtn.style.transform = `translateY(-${yOffset}px)`;
animationId = requestAnimationFrame(animate);
}
animate();
}
function stopAnimation() {
isAnimating = false;
console.log('アニメーション停止 - 位置リセット');
if (animationId) cancelAnimationFrame(animationId);
floatingBtn.style.transform = '';
}
window.addEventListener('scroll', checkScrollPosition);
console.log('初期スクロールチェック開始');
setTimeout(checkScrollPosition, 500);
});
})();
</script>
詳細解説
getElementPosition
のロジック
要素の相対的なoffsetTop
を親要素ごとに加算し、ドキュメント上の絶対 Y 座標を算出。スクロール位置と比較するのに用います。- サイン波アニメーション
Math.sin(time)
は-1~1
の周期関数。そのままだと負値も出るため、(sine+1)/2
で0~1
に正規化し、画面高さの 0~30% の範囲で上下運動を実現。 - パフォーマンス配慮
画面更新ごとに大量ログを吐くと重くなるため、Math.floor(time*10)%20===0
といった条件で間引きログ。 - デバッグログ
・要素検出状況
・スクロールチェック時の各種座標
・アニメーション開始/停止イベント
→本番運用ではコメントアウトまたはconsole.debug
に置き換えると良いでしょう。
応用とカスタマイズ
- 対象エリアを複数設置
複数の開始/終了マーカーを配列で管理し、どのエリアにいるかループで判定可能。 - 表示・非表示切り替え
アニメーションだけでなく、CSS クラス切り替えで完全に非表示にも応用可能。 - レスポンシブ対応
画面幅によってmaxPixels
やspeed
を調整し、モバイルではゆっくり、PCでは高速に動かすなど。
まとめ
この記事では、
- 記事内の特定範囲のみ挙動を変えたい
- 固定要素(
position: fixed
)に対してスクロール連動アニメーションを実装したい
といったニーズを満たすための JavaScript テクニックをご紹介しました。スクロール位置の取得からサイン波アニメーション、デバッグログまでワンストップで実装例を示しています。ぜひ皆さんのサイトでも「あるけど意外とない」動的なフローティングボタンをお試しください!
>>こちらの記事にスクリプトで埋められています。