在OpenCode Zen中测试了一下Ox Alpha Free模型,用的是Max思考强度。
一句话生成
创建一个HTML,内容是SVG绘制一个鹈鹕骑自行车的2D动画,页面顶部区域写ox-alpha


<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ox-alpha · 鹈鹕骑自行车</title>
<style>
:root{
--bg1:#0b1020; --bg2:#141b36;
--ink:#e8edf7;
}
*{ margin:0; padding:0; box-sizing:border-box; }
body{
min-height:100vh;
display:flex; flex-direction:column; align-items:center;
gap:18px; padding:34px 16px 40px;
background:radial-gradient(1200px 600px at 50% -10%, var(--bg2), var(--bg1));
font-family:"Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
color:var(--ink);
}
/* ===== 页面顶部标题区 ===== */
header{ text-align:center; }
h1{
font-size:clamp(2.4rem, 6vw, 3.6rem);
letter-spacing:.12em;
font-weight:800;
background:linear-gradient(92deg,#67e8f9 0%,#a78bfa 55%,#f0abfc 100%);
-webkit-background-clip:text; background-clip:text;
-webkit-text-fill-color:transparent; color:transparent;
filter:drop-shadow(0 0 18px rgba(139,122,255,.35));
}
header p{
margin-top:6px; font-size:.95rem; letter-spacing:.28em;
color:#93a3c8; text-transform:uppercase;
}
/* ===== 场景卡片 ===== */
.card{
width:min(880px, 96vw);
border-radius:18px; overflow:hidden;
border:1px solid rgba(148,163,184,.25);
box-shadow:0 24px 60px rgba(0,0,0,.5), 0 0 0 6px rgba(148,163,184,.06);
background:#dff2ff;
}
svg{ display:block; width:100%; height:auto; }
footer{ font-size:.8rem; color:#64748b; letter-spacing:.15em; }
/* 云朵漂移(纯位移,与坐标原点无关,可安全使用 CSS 动画) */
.cloud{ animation:drift linear infinite; }
.c1{ animation-duration:30s; animation-delay:-6s; }
.c2{ animation-duration:40s; animation-delay:-22s; }
.c3{ animation-duration:52s; animation-delay:-38s; }
@keyframes drift{
from{ transform:translateX(900px); }
to { transform:translateX(-280px); }
}
@media (prefers-reduced-motion: reduce){
.cloud{ animation:none; }
}
</style>
</head>
<body>
<header>
<h1>ox-alpha</h1>
<p>pelican ride · 鹈鹕骑行中</p>
</header>
<div class="card">
<svg id="scene" viewBox="0 0 800 520" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="一只鹈鹕骑着自行车的二维动画">
<defs>
<linearGradient id="sky" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#8fd3ff"/>
<stop offset=".65" stop-color="#c9ecff"/>
<stop offset="1" stop-color="#eaf8ff"/>
</linearGradient>
<radialGradient id="sunGlow">
<stop offset="0" stop-color="#ffe9a8" stop-opacity=".9"/>
<stop offset="1" stop-color="#ffe9a8" stop-opacity="0"/>
</radialGradient>
<linearGradient id="bodyGrad" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="1" stop-color="#dde7f2"/>
</linearGradient>
</defs>
<!-- 天空 -->
<rect width="800" height="520" fill="url(#sky)"/>
<!-- 太阳(光线由 JS 缓慢旋转) -->
<g transform="translate(676 88)">
<circle r="64" fill="url(#sunGlow)"/>
<g id="rays" stroke="#ffd76e" stroke-width="4" stroke-linecap="round">
<line x1="0" y1="-36" x2="0" y2="-48"/>
<line x1="0" y1="36" x2="0" y2="48"/>
<line x1="-36" y1="0" x2="-48" y2="0"/>
<line x1="36" y1="0" x2="48" y2="0"/>
<line x1="-25" y1="-25" x2="-34" y2="-34"/>
<line x1="25" y1="-25" x2="34" y2="-34"/>
<line x1="-25" y1="25" x2="-34" y2="34"/>
<line x1="25" y1="25" x2="34" y2="34"/>
</g>
<circle r="26" fill="#ffd76e" stroke="#f5b83d" stroke-width="3"/>
</g>
<!-- 云朵 -->
<g class="cloud c1" opacity=".95">
<g transform="translate(0 90)" fill="#ffffff">
<ellipse rx="36" ry="17"/><ellipse cx="-28" cy="7" rx="23" ry="12"/><ellipse cx="28" cy="7" rx="25" ry="13"/>
</g>
</g>
<g class="cloud c2" opacity=".85">
<g transform="translate(0 160) scale(.72)" fill="#ffffff">
<ellipse rx="36" ry="17"/><ellipse cx="-28" cy="7" rx="23" ry="12"/><ellipse cx="28" cy="7" rx="25" ry="13"/>
</g>
</g>
<g class="cloud c3" opacity=".9">
<g transform="translate(0 52) scale(.55)" fill="#ffffff">
<ellipse rx="36" ry="17"/><ellipse cx="-28" cy="7" rx="23" ry="12"/><ellipse cx="28" cy="7" rx="25" ry="13"/>
</g>
</g>
<!-- 远山 / 近山(各画两份拼接,JS 横向循环滚动形成视差) -->
<g id="hillsBack" fill="#cdeacd">
<path d="M0 372 Q100 322 200 356 Q300 392 400 350 Q500 316 600 362 Q700 398 800 372 V420 H0 Z"/>
<path transform="translate(800 0)" d="M0 372 Q100 322 200 356 Q300 392 400 350 Q500 316 600 362 Q700 398 800 372 V420 H0 Z"/>
</g>
<g id="hillsFront" fill="#a8dba0">
<path d="M0 396 Q120 344 240 384 Q360 420 480 380 Q600 344 720 392 Q760 402 800 396 V420 H0 Z"/>
<path transform="translate(800 0)" d="M0 396 Q120 344 240 384 Q360 420 480 380 Q600 344 720 392 Q760 402 800 396 V420 H0 Z"/>
</g>
<!-- 草地与公路 -->
<rect y="440" width="800" height="80" fill="#7cc474"/>
<rect y="414" width="800" height="34" fill="#64748b"/>
<rect y="414" width="800" height="4" fill="#94a3b8"/>
<line id="roadDash" x1="-20" y1="431" x2="820" y2="431"
stroke="#f8fafc" stroke-width="5" stroke-linecap="round"
stroke-dasharray="42 30"/>
<!-- 路边小景物(JS 生成并滚动) -->
<g id="props"></g>
<!-- 车影 -->
<ellipse cx="340" cy="419" rx="175" ry="9" fill="#0f172a" opacity=".14"/>
<!-- 追风线(速度感) -->
<g id="wind" stroke="#ffffff" stroke-width="4" stroke-linecap="round" opacity=".55">
<line x1="150" y1="205" x2="205" y2="205"/>
<line x1="120" y1="245" x2="185" y2="245"/>
<line x1="155" y1="285" x2="215" y2="285"/>
</g>
<!-- ============ 自行车与鹈鹕 ============ -->
<!-- 远侧曲柄 / 脚踏 / 腿(画在最底层) -->
<line id="crankFar" x1="320" y1="372" x2="320" y2="372" stroke="#1e293b" stroke-width="5" stroke-linecap="round"/>
<rect id="pedalFar" x="-11" y="-3" width="22" height="6" rx="2" fill="#334155"/>
<path id="legFar" fill="none" stroke="#c2410c" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path id="footFar" d="M-8 -3 C 2 -6 12 -2 21 4 C 10 8 -2 6 -8 3 Z" fill="#c2410c"/>
<!-- 车轮 -->
<g id="wheelRear">
<circle cx="230" cy="368" r="46" fill="none" stroke="#334155" stroke-width="9"/>
<circle cx="230" cy="368" r="40" fill="none" stroke="#cbd5e1" stroke-width="3"/>
<g id="spokesRear" stroke="#94a3b8" stroke-width="2.5"></g>
<circle cx="230" cy="368" r="5.5" fill="#475569"/>
</g>
<g id="wheelFront">
<circle cx="450" cy="368" r="46" fill="none" stroke="#334155" stroke-width="9"/>
<circle cx="450" cy="368" r="40" fill="none" stroke="#cbd5e1" stroke-width="3"/>
<g id="spokesFront" stroke="#94a3b8" stroke-width="2.5"></g>
<circle cx="450" cy="368" r="5.5" fill="#475569"/>
</g>
<!-- 车架 -->
<g stroke="#ef4444" stroke-width="7" stroke-linecap="round" fill="none">
<line x1="230" y1="368" x2="320" y2="372"/> <!-- 后下叉 -->
<line x1="230" y1="368" x2="288" y2="284"/> <!-- 后上叉 -->
<line x1="320" y1="372" x2="288" y2="284"/> <!-- 座管 -->
<line x1="320" y1="372" x2="426" y2="266"/> <!-- 下管 -->
<line x1="290" y1="284" x2="424" y2="266"/> <!-- 上管 -->
<line x1="426" y1="264" x2="450" y2="368"/> <!-- 前叉 -->
</g>
<!-- 车把 -->
<line x1="426" y1="264" x2="420" y2="244" stroke="#334155" stroke-width="6" stroke-linecap="round"/>
<path d="M420 244 C 432 236 446 240 452 250" fill="none" stroke="#334155" stroke-width="6" stroke-linecap="round"/>
<circle cx="452" cy="250" r="4.5" fill="#111827"/>
<!-- 车座 -->
<line x1="320" y1="372" x2="290" y2="286" stroke="#475569" stroke-width="5"/>
<ellipse cx="286" cy="283" rx="25" ry="7" fill="#1f2937" transform="rotate(-8 286 283)"/>
<!-- 链条与牙盘 -->
<line id="chainTop" x1="236" y1="359" x2="309" y2="359" stroke="#64748b" stroke-width="3" stroke-dasharray="5 4"/>
<line id="chainBot" x1="237" y1="381" x2="311" y2="385" stroke="#64748b" stroke-width="3" stroke-dasharray="5 4"/>
<circle cx="320" cy="372" r="15" fill="none" stroke="#475569" stroke-width="5"/>
<circle cx="320" cy="372" r="4" fill="#334155"/>
<!-- 近侧曲柄 / 脚踏 -->
<line id="crankNear" x1="320" y1="372" x2="320" y2="372" stroke="#334155" stroke-width="6" stroke-linecap="round"/>
<rect id="pedalNear" x="-11" y="-3" width="22" height="6" rx="2" fill="#111827"/>
<!-- ============ 鹈鹕(随蹬踏轻微起伏) ============ -->
<g id="bird">
<!-- 尾羽(会小幅摆动) -->
<path id="tail" d="M232 220 L192 202 L206 228 L190 240 L234 244 Z"
fill="#f8fafc" stroke="#cbd5e1" stroke-width="2" stroke-linejoin="round"/>
<!-- 身体 -->
<ellipse cx="282" cy="234" rx="62" ry="44" fill="url(#bodyGrad)"
stroke="#cbd5e1" stroke-width="2" transform="rotate(-10 282 234)"/>
<path d="M256 220 Q 292 206 320 226" fill="none" stroke="#dbe4ee" stroke-width="3" stroke-linecap="round"/>
<!-- 脖子 -->
<path d="M326 216 C 350 198 364 172 380 150" fill="none"
stroke="#f4f8fc" stroke-width="17" stroke-linecap="round"/>
<!-- 头部 -->
<circle cx="385" cy="140" r="17" fill="#ffffff" stroke="#e2e8f0" stroke-width="1.5"/>
<path d="M376 125 q 7 -9 15 -7 q -5 4 -4 9" fill="none" stroke="#cbd5e1" stroke-width="2" stroke-linecap="round"/>
<!-- 眼睛(JS 控制眨眼) -->
<ellipse id="eye" cx="391" cy="133" rx="3.6" ry="3.6" fill="#0f172a"/>
<circle cx="392.4" cy="131.6" r="1.1" fill="#ffffff"/>
<!-- 大嘴:上喙 + 喉囊 -->
<path d="M398 140 C 400 168 424 187 456 183 C 476 180 488 169 492 161 C 462 152 428 144 398 140 Z"
fill="#fbbf24" stroke="#d97706" stroke-width="2" stroke-linejoin="round"/>
<path d="M396 130 C 430 127 468 138 496 156 L 491 163 C 462 151 428 143 396 141 Z"
fill="#f59e0b" stroke="#d97706" stroke-width="2" stroke-linejoin="round"/>
<path d="M404 135 L 426 137" stroke="#b45309" stroke-width="1.6" stroke-linecap="round"/>
<!-- 翅膀扶把 -->
<path d="M306 210 C 352 196 414 214 448 248" fill="none" stroke="#cbd5e1" stroke-width="13" stroke-linecap="round"/>
<path d="M306 210 C 352 196 414 214 448 248" fill="none" stroke="#eef3f8" stroke-width="9" stroke-linecap="round"/>
<path d="M436 240 l 12 4 M 440 232 l 12 2" stroke="#cbd5e1" stroke-width="2.4" stroke-linecap="round"/>
</g>
<!-- 近侧腿与脚(画在身体之上) -->
<path id="legNear" fill="none" stroke="#f97316" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/>
<path id="footNear" d="M-8 -3 C 2 -6 12 -2 21 4 C 10 8 -2 6 -8 3 Z" fill="#f97316"/>
</svg>
</div>
<footer>HTML · SVG · 原生 JS —— 车轮、双腿 IK 蹬踏、视差背景</footer>
<script>
(() => {
'use strict';
const $ = id => document.getElementById(id);
const NS = 'http://www.w3.org/2000/svg';
/* ---------- 几何常量 ---------- */
const BB = { x: 320, y: 372 }; // 中轴(脚踏曲柄中心)
const HIP = { x: 298, y: 282 }; // 髋关节
const CR = 26; // 曲柄长
const L1 = 64, L2 = 64; // 大腿 / 小腿长度
const WHEELS = [ { g:$('wheelRear'), s:$('spokesRear'), cx:230, cy:368 },
{ g:$('wheelFront'), s:$('spokesFront'), cx:450, cy:368 } ];
const R = 46; // 车轮半径
const OMEGA = 2.2; // 角速度 rad/s
const SPEED = OMEGA * R; // 地面滚动速度 px/s
/* ---------- 生成辐条 ---------- */
WHEELS.forEach(w => {
for (let i = 0; i < 8; i++) {
const a = i * Math.PI / 4;
const l = document.createElementNS(NS, 'line');
l.setAttribute('x1', w.cx); l.setAttribute('y1', w.cy);
l.setAttribute('x2', w.cx + Math.cos(a) * 38);
l.setAttribute('y2', w.cy + Math.sin(a) * 38);
w.s.appendChild(l);
}
});
/* ---------- 路边小景物 ---------- */
const propsG = $('props'), props = [];
const rnd = (a, b) => a + Math.random() * (b - a);
function makeProp() {
const g = document.createElementNS(NS, 'g');
const t = Math.random();
if (t < 0.4) { // 草丛
g.innerHTML = '<path d="M-6 0 C -5 -10 -3 -12 -2 -14 M0 0 C 0 -12 1 -16 2 -18 M6 0 C 6 -9 8 -12 9 -13" fill="none" stroke="#3f9142" stroke-width="3" stroke-linecap="round"/>';
} else if (t < 0.75) { // 小花
g.innerHTML = '<line x1="0" y1="0" x2="0" y2="-14" stroke="#3f9142" stroke-width="2.5"/><circle cy="-17" r="5" fill="#f472b6"/><circle cy="-17" r="2" fill="#fde68a"/>';
} else { // 石子
g.innerHTML = '<ellipse rx="7" ry="4" fill="#9ca3af"/>';
}
return g;
}
for (let i = 0; i < 12; i++) {
const g = makeProp(), x = rnd(-40, 860), y = rnd(458, 506);
propsG.appendChild(g);
props.push({ g, x, y });
}
/* ---------- 两骨逆向运动学:已知髋与脚踏位置求膝 ---------- */
function solveKnee(hx, hy, tx, ty) {
let dx = tx - hx, dy = ty - hy;
const len = Math.hypot(dx, dy) || 1e-6;
const ux = dx / len, uy = dy / len;
const d = Math.min(L1 + L2 - 0.01, Math.max(Math.abs(L1 - L2) + 0.01, len));
const a = (L1 * L1 - L2 * L2 + d * d) / (2 * d);
const h = Math.sqrt(Math.max(0, L1 * L1 - a * a));
const mx = hx + ux * a, my = hy + uy * a;
// 两个候选膝盖,取靠前(x 更大)的一个,符合向前弯腿
const k1x = mx - uy * h, k1y = my + ux * h;
const k2x = mx + uy * h, k2y = my - ux * h;
const k = k1x > k2x ? [k1x, k1y] : [k2x, k2y];
return [k[0], k[1], hx + ux * d, hy + uy * d];
}
/* ---------- 元素引用 ---------- */
const legNear = $('legNear'), legFar = $('legFar');
const footNear = $('footNear'), footFar = $('footFar');
const pedalNear = $('pedalNear'), pedalFar = $('pedalFar');
const crankNear = $('crankNear'), crankFar = $('crankFar');
const bird = $('bird'), tail = $('tail'), eye = $('eye');
const roadDash = $('roadDash'), chainTop = $('chainTop'), chainBot = $('chainBot');
const hillsBack = $('hillsBack'), hillsFront = $('hillsFront');
const windLines = [...$('wind').children];
const setAttr = (el, n, v) => el.setAttribute(n, v);
/* ---------- 主循环 ---------- */
const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
let phi = 0.9, t = 0, dashOff = 0, chainOff = 0, hb = 0, hf = 0, last = performance.now();
function update(dt) {
t += dt;
phi = (phi + OMEGA * dt) % (Math.PI * 2);
const deg = phi * 180 / Math.PI;
// 车轮与太阳光线
WHEELS.forEach(w => w.s.setAttribute('transform', `rotate(${deg} ${w.cx} ${w.cy})`));
$('rays').setAttribute('transform', `rotate(${(t * 14) % 360})`);
// 脚踏位置(近 / 远相差 180°),踏板始终保持水平
const nx = BB.x + Math.cos(phi) * CR, ny = BB.y + Math.sin(phi) * CR;
const fx = BB.x + Math.cos(phi + Math.PI) * CR, fy = BB.y + Math.sin(phi + Math.PI) * CR;
// 身体起伏,髋部随之移动
const bob = Math.sin(phi * 2) * 1.6;
bird.setAttribute('transform', `translate(0 ${bob.toFixed(2)})`);
const hx = HIP.x, hy = HIP.y + bob;
// 双腿 IK + 脚掌贴踏板
[[nx, ny, legNear, footNear, crankNear, pedalNear],
[fx, fy, legFar, footFar, crankFar, pedalFar ]].forEach(([px, py, leg, foot, crank, pedal]) => {
const [kx, ky, ax, ay] = solveKnee(hx, hy, px, py);
leg.setAttribute('d', `M${hx} ${hy} L${kx.toFixed(1)} ${ky.toFixed(1)} L${ax.toFixed(1)} ${ay.toFixed(1)}`);
foot.setAttribute('transform', `translate(${ax.toFixed(1)} ${ay.toFixed(1)}) rotate(${(Math.sin(phi) * 6).toFixed(1)})`);
pedal.setAttribute('transform', `translate(${px.toFixed(1)} ${py.toFixed(1)})`);
crank.setAttribute('x2', px.toFixed(1)); crank.setAttribute('y2', py.toFixed(1));
});
// 尾羽摆动 + 眨眼
tail.setAttribute('transform', `rotate(${(Math.sin(t * 6) * 4).toFixed(1)} 230 232)`);
eye.setAttribute('ry', (t % 3.7 < 0.12) ? '0.5' : '3.6');
// 路面虚线 / 链条 / 山峦视差 / 路边景物 / 追风线
dashOff = (dashOff + SPEED * dt) % 72;
roadDash.setAttribute('stroke-dashoffset', dashOff.toFixed(1));
chainOff = (chainOff + SPEED * dt * 0.6) % 9;
chainTop.setAttribute('stroke-dashoffset', (-chainOff).toFixed(1));
chainBot.setAttribute('stroke-dashoffset', chainOff.toFixed(1));
hb = (hb + SPEED * 0.30 * dt) % 800;
hf = (hf + SPEED * 0.55 * dt) % 800;
hillsBack.setAttribute('transform', `translate(${-hb} 0)`);
hillsFront.setAttribute('transform', `translate(${-hf} 0)`);
props.forEach(p => {
p.x -= SPEED * dt;
if (p.x < -70) p.x += 940;
p.g.setAttribute('transform', `translate(${p.x.toFixed(1)} ${p.y})`);
});
windLines.forEach((l, i) => {
let x = parseFloat(l.getAttribute('x1')) - SPEED * 1.6 * dt;
if (x < 60) x += 280;
l.setAttribute('x1', x.toFixed(1));
l.setAttribute('x2', (x + 58).toFixed(1));
});
}
function frame(now) {
const dt = Math.min(0.05, (now - last) / 1000);
last = now;
update(dt);
requestAnimationFrame(frame);
}
if (reduced) {
update(0); // 减少动态偏好:仅渲染一帧静态画面
} else {
requestAnimationFrame(frame);
}
})();
</script>
</body>
</html>