シルクウェーブ
半透明のサイン波リボンを canvas で加算合成し、しなやかに流れる絹のような発光背景を描きます。高級感あるヒーローに。
ライブデモ
使用例(お題: アイドルグループ Sakura)
この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- Sakura:流れる絹波を背に、新曲MVのプレミア告知 -->
<section class="sw-stage">
<!-- ★主役:加算合成で流れるシルクウェーブ -->
<canvas class="sw-canvas" id="swCanvas" aria-hidden="true"></canvas>
<div class="sw-inner">
<span class="sw-eyebrow">NEW MUSIC VIDEO</span>
<h1 class="sw-title">「桜花リフレイン」</h1>
<p class="sw-sub">5th Single ・ Music Video Premiere</p>
<!-- MVプレイヤー風カード -->
<div class="sw-player">
<button class="sw-play" type="button" id="swPlay" aria-label="再生">▶</button>
<div class="sw-player__meta">
<b>桜花リフレイン</b>
<span>3:42 ・ プレミア公開</span>
</div>
</div>
<p class="sw-date">6.20 (土) 20:00 公開</p>
</div>
</section>
CSS
/* Sakura:深い夜桜色に、絹のような発光ウェーブのMV告知 */
* { box-sizing: border-box; margin: 0; padding: 0; }
.sw-stage {
position: relative;
min-height: 400px;
height: 400px;
overflow: hidden;
background: linear-gradient(160deg, #2a0f24 0%, #3d1535 55%, #4a1230 100%);
color: #fff;
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", system-ui, sans-serif;
}
/* ★主役:シルクウェーブを加算合成で描く canvas */
.sw-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.sw-inner {
position: relative;
z-index: 1;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: 0 24px;
}
.sw-eyebrow {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.28em;
color: #ffc6df;
}
.sw-title {
margin-top: 10px;
font-size: 36px;
font-weight: 800;
letter-spacing: 0.04em;
text-shadow: 0 0 28px rgba(255,143,196,0.55);
}
.sw-sub { margin-top: 8px; font-size: 12px; color: rgba(255,255,255,0.82); letter-spacing: 0.06em; }
/* MVプレイヤー風カード */
.sw-player {
margin-top: 18px;
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px 10px 10px;
border-radius: 999px;
background: rgba(255,255,255,0.1);
border: 1px solid rgba(255,255,255,0.25);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
.sw-play {
width: 42px; height: 42px;
border-radius: 50%;
border: none;
cursor: pointer;
font-size: 14px;
color: #4a1230;
background: linear-gradient(135deg, #ffd1e0, #ff9ec6);
box-shadow: 0 6px 16px rgba(255,143,196,0.5);
transition: transform 0.2s ease;
}
.sw-play:hover { transform: scale(1.08); }
.sw-play:active { transform: scale(0.95); }
.sw-player__meta { text-align: left; }
.sw-player__meta b { display: block; font-size: 13px; }
.sw-player__meta span { font-size: 10px; color: rgba(255,255,255,0.75); }
.sw-date { margin-top: 14px; font-size: 12px; font-weight: 700; color: #ffd1e0; }
@media (prefers-reduced-motion: reduce) {
.sw-play { transition: none; }
}
JavaScript
// 半透明のサイン波リボンを加算合成し、流れる絹のような発光を描く
(() => {
const canvas = document.getElementById("swCanvas");
if (!canvas) return; // null安全
const ctx = canvas.getContext("2d");
if (!ctx) return;
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
let w = 0, h = 0, dpr = 1;
const resize = () => {
dpr = Math.min(window.devicePixelRatio || 1, 2);
w = canvas.clientWidth;
h = canvas.clientHeight;
canvas.width = Math.floor(w * dpr);
canvas.height = Math.floor(h * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
window.addEventListener("resize", resize);
// 桜系の発光色リボン
const ribbons = [
{ col: "rgba(255,143,196,0.18)", amp: 42, len: 0.011, sp: 0.6, yoff: 0.45, ph: 0 },
{ col: "rgba(179,156,255,0.16)", amp: 52, len: 0.008, sp: 0.4, yoff: 0.55, ph: 2 },
{ col: "rgba(255,209,224,0.14)", amp: 34, len: 0.013, sp: 0.8, yoff: 0.5, ph: 4 },
{ col: "rgba(127,214,255,0.12)", amp: 60, len: 0.006, sp: 0.3, yoff: 0.62, ph: 1 },
];
// 1本のリボンを塗る
const drawRibbon = (r, t) => {
ctx.beginPath();
ctx.moveTo(0, h);
for (let x = 0; x <= w; x += 8) {
const y = h * r.yoff
+ Math.sin(x * r.len + t * r.sp + r.ph) * r.amp
+ Math.sin(x * r.len * 0.5 + t * r.sp * 0.7) * (r.amp * 0.4);
ctx.lineTo(x, y);
}
ctx.lineTo(w, h);
ctx.closePath();
ctx.fillStyle = r.col;
ctx.fill();
};
const render = (t) => {
ctx.clearRect(0, 0, w, h);
ctx.globalCompositeOperation = "lighter"; // 加算合成で発光感
for (const r of ribbons) drawRibbon(r, t);
ctx.globalCompositeOperation = "source-over";
};
if (reduce) {
render(0); // 静止1枚
return;
}
let raf = 0;
const loop = (ms) => { render(ms / 1000); raf = requestAnimationFrame(loop); };
raf = requestAnimationFrame(loop);
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf);
if (!document.hidden) raf = requestAnimationFrame(loop);
});
// 再生ボタンの軽い反応
const play = document.getElementById("swPlay");
if (play) {
play.addEventListener("click", () => {
const on = play.textContent === "▶";
play.textContent = on ? "❚❚" : "▶";
});
}
})();
コード
HTML
<!-- シルクウェーブ: canvasで重なるサイン波のリボンを描き、絹のような流れを表現 -->
<div class="silk-stage">
<canvas id="silkCanvas" class="silk-canvas"></canvas>
<div class="silk-content">
<h1 class="silk-title">Silk Waves</h1>
<p class="silk-sub">半透明のサイン波リボンを重ねて、しなやかに流れる絹のような背景を描きます。</p>
</div>
</div>
CSS
/* canvasの波の上にテキストを乗せる */
* { box-sizing: border-box; margin: 0; padding: 0; }
.silk-stage {
position: relative;
min-height: 360px;
overflow: hidden;
display: grid;
place-items: center;
background: linear-gradient(160deg, #1a0b2e 0%, #2d0b4e 50%, #0b1b3a 100%);
font-family: "Segoe UI", "Hiragino Sans", system-ui, sans-serif;
}
.silk-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
.silk-content {
position: relative;
z-index: 2;
text-align: center;
color: #fff;
padding: 0 24px;
max-width: 500px;
}
.silk-title {
font-size: 44px;
font-weight: 800;
letter-spacing: 0.04em;
text-shadow: 0 6px 30px rgba(0, 0, 0, 0.45);
}
.silk-sub {
margin-top: 14px;
font-size: 14px;
line-height: 1.85;
color: rgba(255, 255, 255, 0.85);
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
JavaScript
// 複数のサイン波リボンを半透明で重ね描きし、絹のような流れを作る
(() => {
const canvas = document.getElementById("silkCanvas");
if (!canvas || !canvas.getContext) return; // null安全
const ctx = canvas.getContext("2d");
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
let W = 0, H = 0;
const resize = () => {
const r = canvas.getBoundingClientRect();
W = Math.max(1, r.width);
H = Math.max(1, r.height);
canvas.width = Math.floor(W * dpr);
canvas.height = Math.floor(H * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
window.addEventListener("resize", resize);
// リボンごとの色と動きのパラメータ
const ribbons = [
{ hue: 290, amp: 38, len: 0.012, sp: 0.6, yo: 0.40, a: 0.16 },
{ hue: 210, amp: 50, len: 0.009, sp: 0.45, yo: 0.55, a: 0.16 },
{ hue: 330, amp: 30, len: 0.015, sp: 0.8, yo: 0.50, a: 0.14 },
{ hue: 180, amp: 44, len: 0.011, sp: 0.35, yo: 0.62, a: 0.12 },
];
// 1本のリボンを塗りつぶす
const drawRibbon = (rb, s) => {
ctx.beginPath();
ctx.moveTo(0, H);
for (let x = 0; x <= W; x += 8) {
const y = H * rb.yo
+ Math.sin(x * rb.len + s * rb.sp) * rb.amp
+ Math.sin(x * rb.len * 0.5 - s * rb.sp * 0.7) * rb.amp * 0.5;
ctx.lineTo(x, y);
}
ctx.lineTo(W, H);
ctx.closePath();
const g = ctx.createLinearGradient(0, 0, W, 0);
g.addColorStop(0, `hsla(${rb.hue}, 85%, 65%, 0)`);
g.addColorStop(0.5, `hsla(${rb.hue}, 85%, 65%, ${rb.a})`);
g.addColorStop(1, `hsla(${rb.hue + 30}, 85%, 60%, 0)`);
ctx.fillStyle = g;
ctx.fill();
};
const draw = (t) => {
const s = t / 1000;
ctx.clearRect(0, 0, W, H);
ctx.globalCompositeOperation = "lighter"; // 加算合成で発光感
for (const rb of ribbons) drawRibbon(rb, s);
ctx.globalCompositeOperation = "source-over";
};
let raf = 0;
if (reduce) {
draw(0);
} else {
const loop = (t) => { draw(t); raf = requestAnimationFrame(loop); };
raf = requestAnimationFrame(loop);
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf); // 二重ループ防止
if (!document.hidden) raf = requestAnimationFrame(loop);
});
}
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「シルクウェーブ」の効果を追加してください。
# 追加してほしい効果
シルクウェーブ(背景 & グラデーション)
半透明のサイン波リボンを canvas で加算合成し、しなやかに流れる絹のような発光背景を描きます。高級感あるヒーローに。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- シルクウェーブ: canvasで重なるサイン波のリボンを描き、絹のような流れを表現 -->
<div class="silk-stage">
<canvas id="silkCanvas" class="silk-canvas"></canvas>
<div class="silk-content">
<h1 class="silk-title">Silk Waves</h1>
<p class="silk-sub">半透明のサイン波リボンを重ねて、しなやかに流れる絹のような背景を描きます。</p>
</div>
</div>
【CSS】
/* canvasの波の上にテキストを乗せる */
* { box-sizing: border-box; margin: 0; padding: 0; }
.silk-stage {
position: relative;
min-height: 360px;
overflow: hidden;
display: grid;
place-items: center;
background: linear-gradient(160deg, #1a0b2e 0%, #2d0b4e 50%, #0b1b3a 100%);
font-family: "Segoe UI", "Hiragino Sans", system-ui, sans-serif;
}
.silk-canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: block;
}
.silk-content {
position: relative;
z-index: 2;
text-align: center;
color: #fff;
padding: 0 24px;
max-width: 500px;
}
.silk-title {
font-size: 44px;
font-weight: 800;
letter-spacing: 0.04em;
text-shadow: 0 6px 30px rgba(0, 0, 0, 0.45);
}
.silk-sub {
margin-top: 14px;
font-size: 14px;
line-height: 1.85;
color: rgba(255, 255, 255, 0.85);
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}
【JavaScript】
// 複数のサイン波リボンを半透明で重ね描きし、絹のような流れを作る
(() => {
const canvas = document.getElementById("silkCanvas");
if (!canvas || !canvas.getContext) return; // null安全
const ctx = canvas.getContext("2d");
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const dpr = Math.min(window.devicePixelRatio || 1, 2);
let W = 0, H = 0;
const resize = () => {
const r = canvas.getBoundingClientRect();
W = Math.max(1, r.width);
H = Math.max(1, r.height);
canvas.width = Math.floor(W * dpr);
canvas.height = Math.floor(H * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
resize();
window.addEventListener("resize", resize);
// リボンごとの色と動きのパラメータ
const ribbons = [
{ hue: 290, amp: 38, len: 0.012, sp: 0.6, yo: 0.40, a: 0.16 },
{ hue: 210, amp: 50, len: 0.009, sp: 0.45, yo: 0.55, a: 0.16 },
{ hue: 330, amp: 30, len: 0.015, sp: 0.8, yo: 0.50, a: 0.14 },
{ hue: 180, amp: 44, len: 0.011, sp: 0.35, yo: 0.62, a: 0.12 },
];
// 1本のリボンを塗りつぶす
const drawRibbon = (rb, s) => {
ctx.beginPath();
ctx.moveTo(0, H);
for (let x = 0; x <= W; x += 8) {
const y = H * rb.yo
+ Math.sin(x * rb.len + s * rb.sp) * rb.amp
+ Math.sin(x * rb.len * 0.5 - s * rb.sp * 0.7) * rb.amp * 0.5;
ctx.lineTo(x, y);
}
ctx.lineTo(W, H);
ctx.closePath();
const g = ctx.createLinearGradient(0, 0, W, 0);
g.addColorStop(0, `hsla(${rb.hue}, 85%, 65%, 0)`);
g.addColorStop(0.5, `hsla(${rb.hue}, 85%, 65%, ${rb.a})`);
g.addColorStop(1, `hsla(${rb.hue + 30}, 85%, 60%, 0)`);
ctx.fillStyle = g;
ctx.fill();
};
const draw = (t) => {
const s = t / 1000;
ctx.clearRect(0, 0, W, H);
ctx.globalCompositeOperation = "lighter"; // 加算合成で発光感
for (const rb of ribbons) drawRibbon(rb, s);
ctx.globalCompositeOperation = "source-over";
};
let raf = 0;
if (reduce) {
draw(0);
} else {
const loop = (t) => { draw(t); raf = requestAnimationFrame(loop); };
raf = requestAnimationFrame(loop);
document.addEventListener("visibilitychange", () => {
cancelAnimationFrame(raf); // 二重ループ防止
if (!document.hidden) raf = requestAnimationFrame(loop);
});
}
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。