shadcn-ui Skill Prompt 輸入動畫
模擬 AI 聊天框的打字機效果,逐字打出「請利用 shadcn-ui skill 幫我重新設計這個網站」,關鍵詞高亮後顯示傳送按鈕。
AI工具Prompt技巧打字機動畫shadcn
提示詞(可直接修改內容)
import React from "react";
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
} from "remotion";
const colors = {
background: "#0A0E14",
backgroundGradient: "linear-gradient(135deg, #0A0E14 0%, #131A24 100%)",
accent: "#00D4AA",
accentSecondary: "#4DA3FF",
warning: "#FFB547",
};
const fonts = { main: "'Inter', 'Noto Sans TC', sans-serif" };
const LINE1 = "請利用 shadcn-ui skill";
const LINE2 = "幫我重新設計這個網站";
const TOTAL-CHARS = LINE1.length + LINE2.length;
const HIGHLIGHT-KEYWORD = "shadcn-ui skill";
const PREFIX = "請利用 ";
const estimateTextWidth = (text: string, fontSize: number): number => {
let width = 0;
for (const ch of text) {
if (/[\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/.test(ch)) width += fontSize * 1.0;
else if (ch === " ") width += fontSize * 0.28;
else if (ch === "-") width += fontSize * 0.4;
else width += fontSize * 0.55;
}
return width;
};
export const Scene53-ShadcnPrompt: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const boxSpring = spring({ frame: Math.max(0, frame - 5), fps, config: { damping: 12, mass: 0.5, stiffness: 110 } });
const boxScale = interpolate(boxSpring, [0, 1], [0.6, 1]);
const boxOpacity = interpolate(boxSpring, [0, 0.3], [0, 1], { extrapolateRight: "clamp" });
const labelOpacity = interpolate(frame, [15, 30], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
const typeStart = 30, typeEnd = 105;
const charsVisible = Math.floor(interpolate(frame, [typeStart, typeEnd], [0, TOTAL-CHARS], { extrapolateLeft: "clamp", extrapolateRight: "clamp" }));
const line1Visible = Math.min(charsVisible, LINE1.length);
const line2Visible = Math.max(0, charsVisible - LINE1.length);
const displayLine1 = LINE1.slice(0, line1Visible);
const displayLine2 = LINE2.slice(0, line2Visible);
const onLine2 = charsVisible > LINE1.length;
const typingDone = charsVisible >= TOTAL-CHARS;
const cursorVisible = !typingDone || Math.floor(frame * 0.06) % 2 === 0;
const highlightOpacity = interpolate(frame, [110, 125], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
const sendSpring = spring({ frame: Math.max(0, frame - 115), fps, config: { damping: 8, mass: 0.4, stiffness: 150 } });
const sendScale = interpolate(sendSpring, [0, 1], [0.5, 1]);
const sendOpacity = interpolate(sendSpring, [0, 0.3], [0, 1], { extrapolateRight: "clamp" });
const sendPulse = frame > 125 ? interpolate(Math.sin((frame - 125) * 0.12), [-1, 1], [0.85, 1.05]) : 1;
const fadeOut = interpolate(frame, [155, 180], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
const glowPulse = interpolate(Math.sin(frame * 0.04), [-1, 1], [0.03, 0.08]);
const skillGlow = highlightOpacity > 0 ? interpolate(Math.sin(frame * 0.08), [-1, 1], [0.6, 1]) : 0;
return (
<AbsoluteFill style={{ background: colors.backgroundGradient }}>
<div style={{ position: "absolute", top: "45%", left: "50%", width: 900, height: 900, borderRadius: "50%", background: `radial-gradient(circle, ${colors.accentSecondary}0a 0%, transparent 60%)`, transform: "translate(-50%, -50%)", opacity: glowPulse * fadeOut }} />
<AbsoluteFill style={{ display: "flex", alignItems: "center", justifyContent: "center", opacity: fadeOut }}>
<div style={{ opacity: boxOpacity, transform: `scale(${boxScale})` }}>
<svg width="1500" height="420" viewBox="0 0 1000 280">
<defs>
<linearGradient id="s3BoxGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="rgba(255,255,255,0.06)" />
<stop offset="100%" stopColor="rgba(255,255,255,0.02)" />
</linearGradient>
<linearGradient id="s3SendGrad" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor={colors.accent} />
<stop offset="100%" stopColor={colors.accentSecondary} />
</linearGradient>
<filter id="s3Glow">
<feGaussianBlur stdDeviation="6" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
<filter id="s3SkillGlow">
<feGaussianBlur stdDeviation="4" result="blur" />
<feMerge><feMergeNode in="blur" /><feMergeNode in="SourceGraphic" /></feMerge>
</filter>
</defs>
<rect x="0" y="0" width="1000" height="280" rx="24" fill="url(#s3BoxGrad)" stroke="rgba(255,255,255,0.1)" strokeWidth="2" />
<rect x="30" y="65" width="940" height="145" rx="16" fill="rgba(0,0,0,0.3)" stroke={`${colors.accent}30`} strokeWidth="1.5" />
<text x="55" y="50" fontSize="22" fontFamily={fonts.main} fill={colors.accent} fontWeight="700" letterSpacing="2" opacity={labelOpacity}>Prompt:</text>
{line1Visible > 0 && (
<g>
{highlightOpacity > 0 && line1Visible === LINE1.length ? (
<>
<text x="60" y="120" fontSize="36" fontFamily={fonts.main} fill="rgba(255,255,255,0.9)" fontWeight="600">{PREFIX}</text>
{(() => {
const prefixW = estimateTextWidth(PREFIX, 36);
const hlW = estimateTextWidth(HIGHLIGHT-KEYWORD, 36);
return (
<>
<rect x={60 + prefixW - 6} y="92" width={hlW + 12} height="42" rx="8" fill={`${colors.accentSecondary}15`} stroke={colors.accentSecondary} strokeWidth="2" opacity={highlightOpacity * skillGlow} filter="url(#s3SkillGlow)" />
<text x={60 + prefixW} y="120" fontSize="36" fontFamily={fonts.main} fill={colors.accentSecondary} fontWeight="700" opacity={Math.max(0.9, highlightOpacity)}>{HIGHLIGHT-KEYWORD}</text>
</>
);
})()}
</>
) : (
<text x="60" y="120" fontSize="36" fontFamily={fonts.main} fill="rgba(255,255,255,0.9)" fontWeight="600">{displayLine1}</text>
)}
</g>
)}
{line2Visible > 0 && <text x="60" y="175" fontSize="36" fontFamily={fonts.main} fill="rgba(255,255,255,0.9)" fontWeight="600">{displayLine2}</text>}
{cursorVisible && !typingDone && <rect x={onLine2 ? 60 + estimateTextWidth(displayLine2, 36) : 60 + estimateTextWidth(displayLine1, 36)} y={onLine2 ? 147 : 92} width="3" height="38" rx="1.5" fill={colors.accent} />}
{sendOpacity > 0 && (
<g transform={`translate(880, 115) scale(${sendScale * sendPulse})`} opacity={sendOpacity}>
<circle cx="20" cy="20" r="22" fill="url(#s3SendGrad)" filter="url(#s3Glow)" />
<path d="M12 20 L28 20 M22 14 L28 20 L22 26" fill="none" stroke="white" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" />
</g>
)}
<circle cx="50" cy="255" r="3" fill={`${colors.accent}30`} />
<circle cx="65" cy="255" r="3" fill={`${colors.accentSecondary}30`} />
<circle cx="80" cy="255" r="3" fill={`${colors.warning}30`} />
</svg>
</div>
</AbsoluteFill>
</AbsoluteFill>
);
};登入後查看完整程式碼