キネティックタイポグラフィ映画:ESCAPE THE ORBIT
フリーのプロンプト百科事典 Wikiprompt より
キネティックタイポグラフィ映画:ESCAPE THE ORBIT JFKのスピーチに基づく20秒のキネティックタイポグラフィ映画を作成するための詳細なプロンプト。正確なアートディレクション、ストーリーボード、技術的実装要件を含む。
関連メディア
プロンプト内容保存
🌐
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ESCAPE THE ORBIT</title>
<style>
body {
margin: 0;
background: #000;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
canvas {
width: min(90vw, 90vh);
height: min(90vw, 90vh);
background: #080D23;
display: block;
box-shadow: 0 0 40px rgba(0,0,0,0.8);
}
</style>
</head>
<body>
<canvas id="c" width="1080" height="1080"></canvas>
<script>
(() => {
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
// ---------- 固定設定 ----------
const W = 1080, H = 1080;
const MIDX = 540, MIDY = 540;
const FONT_SPEECH = '700 64px "Arial Black", "Helvetica Neue", sans-serif';
const FONT_ITALIC = 'italic 700 72px "Georgia", "Times New Roman", serif';
const FONT_ATTRIB = '400 24px "Courier New", monospace';
// 色
const COL_BG = '#080D23';
const COL_TEXT = '#FFF4DD';
const COL_TRAJ = '#FF654F';
const COL_ORBIT = '#6685FF';
// 表示テキスト(順序固定)
const PHRASES = [
"We choose to go to the moon",
"[…]",
"not because they are easy,",
"but because they are hard"
];
const ATTRIB = "、 JFK, Rice Univ., 9/12/1962";
// 軌道パラメータ(解析的)
const ELLIPSE_A = 420; // 長半径
const ELLIPSE_B = 180; // 短半径
const ELLIPSE_CX = MIDX;
const ELLIPSE_CY = MIDY + 80;
const LUNAR_OFFSET = 160; // 焦点距離 (c)
const LUNAR_X = ELLIPSE_CX - LUNAR_OFFSET; // 遠点焦点
// 脱出軌道(直線)開始点: 楕円上の角度 theta_escape = 0.8 rad (右下)
const ESC_THETA = 0.8;
const ESC_START_X = ELLIPSE_CX + ELLIPSE_A * Math.cos(ESC_THETA);
const ESC_START_Y = ELLIPSE_CY + ELLIPSE_B * Math.sin(ESC_THETA);
const ESC_DIR_X = 0.7; // 右上方向
const ESC_DIR_Y = -0.6;
const ESC_SPEED = 1.0;
// カメラ操作: プルバック, ロール, 速度マッチ
// タイムライン (秒)
const T0 = 0, T1 = 3.5, T2 = 6.0, T3 = 7.0, T4 = 10.5, T5 = 14.5, T6 = 17.0, T7 = 20.0;
// グリフキャッシュ(簡易)
const glyphCache = new Map();
function getGlyphWidth(ch, font) {
ctx.font = font;
return ctx.measureText(ch).width;
}
// テキスト全体の幅
function textWidth(str, font) {
ctx.font = font;
return ctx.measureText(str).width;
}
// 軌道上の位置 (t: 0..1 で楕円一周)
function ellipsePos(t) {
const angle = 2 * Math.PI * t;
return {
x: ELLIPSE_CX + ELLIPSE_A * Math.cos(angle),
y: ELLIPSE_CY + ELLIPSE_B * Math.sin(angle)
};
}
// 軌道の接線角度 (rad)
function ellipseTangent(t) {
const angle = 2 * Math.PI * t;
const dx = -ELLIPSE_A * Math.sin(angle);
const dy = ELLIPSE_B * Math.cos(angle);
return Math.atan2(dy, dx);
}
// 脱出直線上の位置 (s: 0..1)
function escapePos(s) {
return {
x: ESC_START_X + ESC_DIR_X * s * 500,
y: ESC_START_Y + ESC_DIR_Y * s * 500
};
}
// 脱出直線の接線角度
const escapeTangent = Math.atan2(ESC_DIR_Y, ESC_DIR_X);
// カメラロール (rad) 10.5s→14.5s で70度
function cameraRoll(t) {
if (t < T4) return 0;
if (t > T5) return 70 * Math.PI / 180;
const p = (t - T4) / (T5 - T4);
return 70 * Math.PI / 180 * p;
}
// カメラプルバック: 3.5s→6.0s で scale 1→0.7
function cameraScale(t) {
if (t < T1) return 1.0;
if (t > T2) return 0.7;
const p = (t - T1) / (T2 - T1);
return 1.0 - 0.3 * p;
}
// 速度マッチ: 14.5s以降、アクティブフレーズを中心に固定
function velocityMatchOffset(t) {
if (t < T5) return {x: 0, y: 0};
// 脱出直線に沿って移動するが、カメラが追従するので表示位置はほぼ中央
// ここではシンプルに、脱出位置から少しずらす
const s = (t - T5) * 0.5;
const p = escapePos(s);
return {x: MIDX - p.x, y: MIDY - p.y};
}
// アクティブなフレーズの表示位置と角度を決定
function getActivePhraseState(t, phraseIndex) {
// 各フレーズの表示時間帯
if (phraseIndex === 0) {
// 0-3.5: 中央静止, 3.5-6: 楕円軌道を移動
if (t < T1) {
return {x: MIDX, y: MIDY, angle: 0, scale: 1};
} else if (t < T2) {
const p = (t - T1) / (T2 - T1);
const orbitT = 0.1 + p * 0.3; // 楕円上の位置
const pos = ellipsePos(orbitT);
const tangent = ellipseTangent(orbitT);
// 読み終わった後は接線方向に傾く
const angle = (t > T1 + 0.5) ? tangent : 0;
return {x: pos.x, y: pos.y, angle: angle, scale: 1};
} else {
// 6s以降は楕円上で静止 (次のフレーズのため)
const pos = ellipsePos(0.4);
return {x: pos.x, y: pos.y, angle: ellipseTangent(0.4), scale: 1};
}
} else if (phraseIndex === 1) { // "[…]"
// 6-7s 中央表示
if (t >= T2 && t < T3) {
return {x: MIDX, y: MIDY, angle: 0, scale: 1};
} else {
return null;
}
} else if (phraseIndex === 2) { // "not because they are easy,"
// 7-10.5s 楕円の近点側で表示
if (t >= T3 && t < T4) {
const p = (t - T3) / (T4 - T3);
const orbitT = 0.55 + p * 0.2;
const pos = ellipsePos(orbitT);
const tangent = ellipseTangent(orbitT);
return {x: pos.x, y: pos.y, angle: tangent, scale: 1};
} else {
return null;
}
} else if (phraseIndex === 3) { // "but because they are hard"
// 10.5-14.5 変形前, 14.5以降 脱出
if (t >= T4 && t < T5) {
// 水平表示
return {x: MIDX, y: MIDY, angle: 0, scale: 1};
} else if (t >= T5 && t <= T7) {
// 脱出直線に乗る
const s = (t - T5) * 0.8;
const pos = escapePos(s);
const angle = escapeTangent;
return {x: pos.x, y: pos.y, angle: angle, scale: 1};
}
}
return null;
}
// 描画: フレーム n (0..1199) t = n/60
function renderFrame(t) {
ctx.clearRect(0, 0, W, H);
ctx.fillStyle = COL_BG;
ctx.fillRect(0, 0, W, H);
// カメラ変換
const scale = cameraScale(t);
const roll = cameraRoll(t);
const vOffset = velocityMatchOffset(t);
ctx.save();
ctx.translate(MIDX + vOffset.x, MIDY + vOffset.y);
ctx.rotate(roll);
ctx.scale(scale, scale);
ctx.translate(-MIDX, -MIDY);
// 軌道描画 (楕円)
ctx.strokeStyle = COL_ORBIT;
ctx.lineWidth = 2;
ctx.setLineDash([10, 12]);
ctx.beginPath();
ctx.ellipse(ELLIPSE_CX, ELLIPSE_CY, ELLIPSE_A, ELLIPSE_B, 0, 0, 2 * Math.PI);
ctx.stroke();
ctx.setLineDash([]);
// 脱出軌道 (直線) 14.5s以降のみ表示
if (t >= T4) {
ctx.strokeStyle = COL_TRAJ;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(ESC_START_X, ESC_START_Y);
ctx.lineTo(ESC_START_X + ESC_DIR_X * 700, ESC_START_Y + ESC_DIR_Y * 700);
ctx.stroke();
}
// 月マーク (焦点)
ctx.fillStyle = COL_TEXT;
ctx.beginPath();
ctx.arc(LUNAR_X, ELLIPSE_CY, 12, 0, 2 * Math.PI);
ctx.fill();
// クレセント (17s以降)
if (t >= T6) {
ctx.fillStyle = COL_BG;
ctx.beginPath();
ctx.arc(LUNAR_X + 8, ELLIPSE_CY - 4, 12, 0, 2 * Math.PI);
ctx.fill();
}
// フレーズ描画
// フレーズ0: 0-6s
if (t < T2) {
const state = getActivePhraseState(t, 0);
if (state) {
drawPhrase(PHRASES[0], state.x, state.y, state.angle, FONT_SPEECH, COL_TEXT);
}
}
// フレーズ1: 6-7s
if (t >= T2 && t < T3) {
const state = getActivePhraseState(t, 1);
if (state) {
drawPhrase(PHRASES[1], state.x, state.y, state.angle, FONT_SPEECH, COL_TEXT);
}
}
// フレーズ2: 7-10.5s
if (t >= T3 && t < T4) {
const state = getActivePhraseState(t, 2);
if (state) {
drawPhrase(PHRASES[2], state.x, state.y, state.angle, FONT_SPEECH, COL_TEXT);
}
}
// フレーズ3: 10.5-20s
if (t >= T4) {
const state = getActivePhraseState(t, 3);
if (state) {
// 最後の "hard" はイタリック
if (t >= T4 && t < T5) {
drawPhrase(PHRASES[3], state.x, state.y, state.angle, FONT_SPEECH, COL_TEXT);
} else {
// 脱出時は "hard" をイタリックで強調
const words = PHRASES[3].split(' ');
const last = words.pop();
const base = words.join(' ');
drawPhrase(base + ' ', state.x - 30, state.y, state.angle, FONT_SPEECH, COL_TEXT);
drawPhrase(last, state.x + textWidth(base + ' ', FONT_SPEECH) - 30, state.y, state.angle, FONT_ITALIC, COL_TRAJ);
}
}
}
// 帰属表示 (14.5s以降)
if (t >= T5) {
ctx.font = FONT_ATTRIB;
ctx.fillStyle = '#FFFFFF80';
ctx.fillText(ATTRIB, 60, H - 60);
}
ctx.restore();
// 最終ホールド (19s以降は完全静止)
if (t >= 19.0) {
// 何もしない (既に描画済み)
}
}
function drawPhrase(text, x, y, angle, font, color) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.font = font;
ctx.fillStyle = color;
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, 0, 0);
ctx.restore();
}
// シーク関数
function seek(t) {
if (t < 0) t = 0;
if (t > 20) t = 20;
renderFrame(t);
}
function frame(n) {
const t = n / 60;
seek(t);
}
// アニメーションループ (60fps)
let startTime = null;
function loop(ts) {
if (!startTime) startTime = ts;
const elapsed = (ts - startTime) / 1000;
const t = elapsed % 20;
renderFrame(t);
requestAnimationFrame(loop);
}
// 初期表示
renderFrame(0);
// 公開API
window.ready = new Promise((resolve) => {
// 即座に解決
resolve();
});
window.seek = seek;
window.frame = frame;
// アニメーション開始
requestAnimationFrame(loop);
})();
</script>
</body>
</html>
ログインして完全なプロンプトを表示
次で続行:
ログインすると、次に同意したことになります: 利用規約 と プライバシーポリシー
使い方
このプロンプトは creative 向けに設計されています。上の内容をコピーして、お好みの AI ツールに貼り付けてください。
最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。
ノート
0 件のコメント