
Webサイトを訪れた瞬間にユーザーの目を引き、ブランドの世界観を瞬時に伝えるために有効なのが、ファーストビュー(ヒーローエリア)への動画配置です。今回解説する「動画ファーストビューモジュール」を導入すれば、エンジニアの手を借りずに、管理画面から動画のアップロードや挙動の細かな制御が可能になります。


Webサイトを訪れた瞬間にユーザーの目を引き、ブランドの世界観を瞬時に伝えるために有効なのが、ファーストビュー(ヒーローエリア)への動画配置です。今回解説する「動画ファーストビューモジュール」を導入すれば、エンジニアの手を借りずに、管理画面から動画のアップロードや挙動の細かな制御が可能になります。
このモジュールの主な目的は、「誰でも簡単に、高品質なフルスクリーン動画背景やフローティングプレイヤー機能をサイトのトップに構築すること」です。
主なメリットは以下の通りです。
新規ページの作成から、提供されたコードの適用、現場での運用設定までの全工程を解説します。
HubSpot管理画面の「コンテンツ」メニューから新しいページ(ランディングページなど)を立ち上げます。エディター左側のサイドバーで作成したモジュール名を検索し、編集エリアの最上部(ファーストビュー)へドラッグ&ドロップして配置します。
高度な動画制御(オートプレイやフローティング)を有効にするため、デザインマネージャーで以下の作業を行います。
コード適用後は、編集画面のサイドパネルからノーコードで詳細を調整できます。
HubSpotのDesign Managerで「新規モジュール」を作成し、以下のコードをそれぞれの箇所に貼り付けてください。
フィールド定義です。繰り返しフィールド(Items)と、スタイル設定用フィールド(Styles)で構成されています。
[
{
"picker": "file",
"display_width": null,
"id": "85f1fb81-9630-d95c-8044-f0d56394c9b8",
"label": "video_file",
"locked": false,
"name": "video_file",
"required": false,
"type": "file"
},
{
"default": false,
"display": "checkbox",
"display_width": null,
"id": "95362a6b-3ac2-608f-3c46-947fd1f71fd7",
"label": "autoplay",
"locked": false,
"name": "autoplay",
"required": false,
"type": "boolean"
},
{
"default": false,
"display": "checkbox",
"display_width": null,
"id": "771f1aed-83f3-dfb2-395d-b5b0f1607d2b",
"label": "loop_video",
"locked": false,
"name": "loop_video",
"required": false,
"type": "boolean"
},
{
"default": false,
"display": "checkbox",
"display_width": null,
"id": "2f8d7054-9948-7913-9bad-b16c8976b16d",
"label": "enable_floating",
"locked": false,
"name": "enable_floating",
"required": false,
"type": "boolean"
},
{
"default": {
"size_type": "auto",
"src": "",
"alt": null,
"loading": "lazy"
},
"display_width": null,
"id": "a02f8772-c972-036b-1f2d-ae697f000c31",
"label": "poster_image",
"locked": false,
"name": "poster_image",
"required": false,
"resizable": true,
"responsive": true,
"show_loading": false,
"type": "image"
}
]<!-- HubSpot カスタム動画モジュール -->
{% set video_src = "" %}
{% if module.video_file %}
{% if module.video_file.url %}
{% set video_src = module.video_file.url %}
{% elif module.video_file.src %}
{% set video_src = module.video_file.src %}
{% elif module.video_file is string %}
{% set video_src = module.video_file %}
{% endif %}
{% endif %}
{% if video_src %}
<div class="custom-video-player" id="videoPlayerContainer-{{ name }}"
data-loop="{{ module.loop_video }}"
data-floating="{{ module.enable_floating }}">
<div class="video-wrapper">
<video
id="customVideo-{{ name }}"
class="video-element"
{% if module.autoplay %}autoplay{% endif %}
muted
{% if module.loop_video %}loop{% endif %}
playsinline
preload="auto"
webkit-playsinline="true"
crossorigin="anonymous"
{% if module.poster_image.src %}poster="{{ module.poster_image.src }}"{% endif %}
>
<source src="{{ video_src }}" type="video/mp4">
</video>
<div class="play-button-overlay" id="playButtonOverlay-{{ name }}">
<button class="play-button" id="playButton-{{ name }}" aria-label="動画を再生">
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<svg class="pause-icon" viewBox="0 0 24 24" fill="currentColor" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
</button>
</div>
<div class="progress-bar" id="progressBar-{{ name }}">
<div class="progress-fill" id="progressFill-{{ name }}"></div>
</div>
</div>
{% if module.enable_floating %}
<div class="floating-player" id="floatingPlayer-{{ name }}" style="display: none;">
<div class="floating-video-wrapper">
<video id="floatingVideo-{{ name }}" class="floating-video-element" muted {% if module.loop_video %}loop{% endif %} playsinline crossorigin="anonymous">
<source src="{{ video_src }}" type="video/mp4">
</video>
<div class="floating-play-button-overlay" id="floatingPlayButtonOverlay-{{ name }}">
<button class="floating-play-button" id="floatingPlayButton-{{ name }}" aria-label="動画を再生">
<svg class="play-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>
<svg class="pause-icon" viewBox="0 0 24 24" fill="currentColor" style="display: none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg>
</button>
</div>
<button class="floating-close-button" id="floatingCloseButton-{{ name }}" aria-label="フローティングプレイヤーを閉じる">
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</div>
</div>
{% endif %}
</div>
{% else %}
<div style="padding: 40px; text-align: center; background: #f5f5f5; border-radius: 12px;">
<p style="color: #666; margin: 0;">動画ファイルを選択してください</p>
</div>
{% endif %}.custom-video-player {
position: relative; max-width: 100%; margin: 0 auto; background: #000; border-radius: 12px; overflow: hidden;
}
.video-wrapper { position: relative; width: 100%; background: #000; min-height: 200px; display: flex; justify-content: center; align-items: center; }
.video-element { max-width: 100%; max-height: 70vh; object-fit: contain; }
.play-button {
width: 60px; height: 60px; border-radius: 50%; background: rgba(255, 255, 255, 0.9); border: none; cursor: pointer;
}
.progress-bar { position: absolute; bottom: 0; left: 0; width: 100%; height: 4px; background: rgba(255, 255, 255, 0.3); cursor: pointer; }
.progress-fill { height: 100%; width: 0%; background: linear-gradient(90deg, #ff6b6b, #ff8e8e); }
/* フローティングプレイヤー */
.floating-player {
position: fixed; bottom: 20px; right: 20px; width: 320px; height: 180px; z-index: 1000;
border-radius: 12px; overflow: hidden; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4); background: #000;
}
.floating-player.show { animation: fadeIn 0.3s ease forwards; }
@keyframes fadeIn { from { opacity: 0; transform: translateX(100%); } to { opacity: 1; transform: translateX(0); } }(function() {
'use strict';
function CustomVideoPlayer(containerId) {
var self = this;
this.suffix = containerId.replace('videoPlayerContainer-', '');
this.container = document.getElementById(containerId);
this.video = document.getElementById('customVideo-' + this.suffix);
this.floatingPlayer = document.getElementById('floatingPlayer-' + this.suffix);
this.floatingVideo = document.getElementById('floatingVideo-' + this.suffix);
this.enableFloating = this.container.getAttribute('data-floating') === 'true';
this.init = function() {
if (!self.video) return;
this.setupEventListeners();
if (this.enableFloating) this.setupScrollDetection();
};
this.setupEventListeners = function() {
this.video.addEventListener('click', function() { self.togglePlay(); });
if (document.getElementById('playButton-' + this.suffix)) {
document.getElementById('playButton-' + this.suffix).addEventListener('click', function(e) {
e.stopPropagation(); self.togglePlay();
});
}
this.video.addEventListener('timeupdate', function() { self.updateProgress(); });
};
this.setupScrollDetection = function() {
if (typeof IntersectionObserver !== 'undefined') {
var observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (!entry.isIntersecting && !self.video.paused) {
self.showFloatingPlayer();
} else if (entry.isIntersecting && self.isFloating) {
self.hideFloatingPlayer();
}
});
}, { threshold: 0.1 });
observer.observe(self.container);
}
};
this.togglePlay = function() {
if (this.video.paused) this.video.play();
else this.video.pause();
};
this.showFloatingPlayer = function() {
this.isFloating = true;
this.video.pause();
this.floatingPlayer.style.display = 'block';
this.floatingPlayer.classList.add('show');
this.floatingVideo.currentTime = this.video.currentTime;
this.floatingVideo.play();
};
this.init();
}
function initializeAllPlayers() {
document.querySelectorAll('[id^="videoPlayerContainer-"]').forEach(function(container) {
if (!container.hasAttribute('data-initialized')) {
new CustomVideoPlayer(container.id);
container.setAttribute('data-initialized', 'true');
}
});
}
window.addEventListener('load', initializeAllPlayers);
})();はい。コード内に muted および playsinline 属性が含まれているため、最新のスマートフォンブラウザでも自動再生が可能です。
可能です。モジュールの設定パネル、またはCSSの max-height などの数値を調整することで、画面全体(100vh)にするか、特定の高さに抑えるかを変更できます。
本モジュールはHubSpotに直接アップロードされたMP4ファイル等の直接参照に最適化されています。YouTube動画を使用する場合は、HubSpot標準の動画モジュール、または外部埋め込み用コードの活用をお勧めします。