使用opencode制作网页,生成美丽的分形图案

16 26-09-08 15:34


opencode是一种AI代码生成工具,相比于传统AI一问一答操作,opencode能够自动运行、检查、修改代码,效率提升极大。

使用以下提示词“julia集合是一种基于复数的分形集合,编写另一个html文件,生成不同C值的julia集合,要求包含两个滑块设置C值,包含一个输入框决定绘制分辨率”,就可以生成一个漂亮的网页,感兴趣的试试吧。

html代码如下

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" cOntent="width=device-width, initial-scale=1.0">
<title>Julia 集合生成器</title>
<style>
  :root{
    --bg1:#0b1020; --bg2:#101a33; --card:rgba(255,255,255,.055);
    --line:rgba(255,255,255,.13); --tx:#e7edf8; --mut:#93a6c4;
    --cy:#22d3ee; --pi:#f472b6; --am:#fbbf24; --gr:#4ade80;
  }
  *{box-sizing:border-box; margin:0; padding:0}
  body{
    font-family:"Segoe UI","Microsoft YaHei","PingFang SC",sans-serif;
    background:linear-gradient(160deg,var(--bg1),var(--bg2) 60%,#0d1830);
    color:var(--tx); min-height:100vh;
  }
  .wrap{max-width:1060px; margin:0 auto; padding:28px 22px 60px}

  header{text-align:center; padding:18px 0 26px}
  .kicker{color:var(--cy); letter-spacing:.3em; font-size:12px; font-weight:600}
  h1{
    font-size:clamp(26px,4.4vw,44px); font-weight:800; margin:10px 0 6px;
    background:linear-gradient(90deg,#22d3ee,#4ade80,#fbbf24,#f472b6);
    -webkit-background-clip:text; background-clip:text; color:transparent;
  }
  .sub{color:var(--mut); font-size:14.5px}
  .sub code{color:var(--cy); font-family:Consolas,monospace; background:rgba(34,211,238,.1); padding:1px 7px; border-radius:6px}

  .panel{
    background:var(--card); border:1px solid var(--line); border-radius:16px;
    padding:18px 22px; margin-bottom:18px;
  }
  .grid{display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:18px}
  .ctl{}
  .ctl label{display:flex; justify-content:space-between; align-items:baseline; margin-bottom:8px}
  .ctl label .name{font-weight:700; font-size:14px}
  .ctl label .val{
    font-family:Consolas,monospace; color:var(--am); font-weight:700; font-size:15px;
    background:rgba(251,191,36,.1); padding:2px 10px; border-radius:8px;
  }
  input[type=range]{
    -webkit-appearance:none; appearance:none; width:100%; height:6px; border-radius:99px;
    background:linear-gradient(90deg,rgba(34,211,238,.5),rgba(244,114,182,.5)); outline:none;
  }
  input[type=range]::-webkit-slider-thumb{
    -webkit-appearance:none; appearance:none; width:18px; height:18px; border-radius:50%;
    background:#fff; border:3px solid var(--cy); cursor:pointer;
  }
  input[type=number]{
    width:100%; padding:9px 12px; border-radius:10px; border:1px solid var(--line);
    background:rgba(0,0,0,.3); color:var(--tx); font-size:15px; font-family:Consolas,monospace; outline:none;
  }
  input[type=number]:focus{border-color:var(--cy)}

  .presets{display:flex; flex-wrap:wrap; gap:8px; margin-top:14px}
  .presets span{
    padding:6px 13px; border-radius:999px; border:1px solid var(--line); font-size:13px;
    cursor:pointer; color:#c6d6ec; transition:.18s;
  }
  .presets span:hover{border-color:var(--pi); color:var(--pi)}

  .canvas-box{
    position:relative; text-align:center;
  }
  canvas{
    width:min(760px,100%); height:auto; border-radius:16px; border:1px solid var(--line);
    background:#04050c; box-shadow:0 18px 60px rgba(0,0,0,.45);
  }
  .statusbar{
    display:flex; flex-wrap:wrap; gap:8px 24px; justify-content:center; margin-top:14px;
    color:var(--mut); font-size:13.5px; font-family:Consolas,monospace;
  }
  .statusbar b{color:var(--cy); font-weight:600}
  .statusbar em{color:var(--gr); font-style:normal; font-weight:700}
  .hint{text-align:center; color:var(--mut); font-size:13px; margin-top:10px}

  .btn{
    padding:8px 20px; border-radius:12px; border:1px solid var(--line); background:rgba(255,255,255,.06);
    color:var(--tx); cursor:pointer; font-size:14px; font-weight:600; transition:.2s; font-family:inherit;
  }
  .btn.primary{background:linear-gradient(90deg,#0ea5e9,#22d3ee); color:#05242e; border:none}
  .btn.primary:hover{filter:brightness(1.1)}
  .btn:hover{border-color:var(--cy); color:var(--cy)}
  details.notes{border:1px solid var(--line); border-radius:14px; margin-top:20px; background:rgba(0,0,0,.18); overflow:hidden}
  details.notes summary{padding:13px 20px; cursor:pointer; font-weight:700; color:var(--cy)}
  details.notes .inner{padding:4px 20px 16px; color:#c2d2e8; font-size:14px; line-height:1.8}
  details.notes code{background:rgba(34,211,238,.12); color:var(--cy); padding:1px 6px; border-radius:6px; font-family:Consolas,monospace}
</style>
</head>
<body>
<div class="wrap">

  <header>
    <div class="kicker">FRACTAL · COMPLEX DYNAMICS</div>
    <h1>Julia 集合生成器</h1>
    <div class="sub">迭代 <code>z ← z² + c</code>,用两个滑块调节复数 <code>c = a + bi</code>,输入框控制绘制分辨率</div>
  </header>

  <div class="panel">
    <div class="grid">
      <div class="ctl">
        <label>
          <span class="name">C 实部 a</span>
          <span class="val" id="reVal">-0.8</span>
        </label>
        <input type="range" id="cRe" min="-2" max="2" step="0.001" value="-0.8">
      </div>
      <div class="ctl">
        <label>
          <span class="name">C 虚部 b</span>
          <span class="val" id="imVal">0.156</span>
        </label>
        <input type="range" id="cIm" min="-2" max="2" step="0.001" value="0.156">
      </div>
      <div class="ctl">
        <label>
          <span class="name">分辨率(每边像素)</span>
          <span class="val" id="resVal">800</span>
        </label>
        <input type="number" id="resInput" min="128" max="2000" step="8" value="800">
      </div>
      <div class="ctl">
        <label>
          <span class="name">最大迭代次数</span>
          <span class="val" id="iterVal">180</span>
        </label>
        <input type="range" id="iterInput" min="20" max="1000" step="10" value="180">
      </div>
    </div>
    <div class="presets">
      <span data-re="-0.8" data-im="0.156">经典 -0.8+0.156i</span>
      <span data-re="-0.74543" data-im="0.11301">夹心蛋糕</span>
      <span data-re="-0.70176" data-im="-0.3842">海马形</span>
      <span data-re="-0.4" data-im="0.6">花椰菜</span>
      <span data-re="0.285" data-im="0.01">红豆</span>
      <span data-re="-0.835" data-im="-0.2321">蝴蝶结</span>
      <span data-re="0.7885" data-im="-0.2">网络星云</span>
    </div>
  </div>

  <div class="canvas-box">
    <canvas id="cv"></canvas>
    <div class="statusbar">
      <span>C = <b id="statusC">-0.8 + 0.156i</b></span>
      <span>分辨率 <b id="statusRes">800×800</b></span>
      <span>迭代上限 <b id="statusIter">180</b></span>
      <span>渲染耗时 <em id="statusTime">—</em></span>
    </div>
    <div class="hint">鼠标在图上移动:实时显示该点复坐标及逃逸迭代次数</div>
    <div class="hint" id="hoverInfo"></div>
  </div>

  <details class="notes">
    <summary>数学说明</summary>
    <div class="inner">
      对每个像素对应的复数 <code>z₀ = x + iy</code>,反复迭代 <code>zₙ₊₁ = zₙ² + c</code>。
      若迭代值始终不逃出圆盘 <code>|z| &gt; 2</code>,该点属于 Julia 集(深色)。
      否则记录逃逸所需的迭代次数并平滑映射为颜色,形成外部的分形轮廓。
      不同 <code>c</code> 值会生成截然不同的形状:连接、破碎、星云、海马……
    </div>
  </details>
</div>

<script>
(function(){
  var XMIN=-1.8, XMAX=1.8, YMIN=-1.8, YMAX=1.8;

  var cv=document.getElementById(&#39;cv&#39;);
  var ctx=cv.getContext(&#39;2d&#39;);
  var reSlider=document.getElementById(&#39;cRe&#39;);
  var imSlider=document.getElementById(&#39;cIm&#39;);
  var reValEl=document.getElementById(&#39;reVal&#39;);
  var imValEl=document.getElementById(&#39;imVal&#39;);
  var resInput=document.getElementById(&#39;resInput&#39;);
  var resValEl=document.getElementById(&#39;resVal&#39;);
  var iterInput=document.getElementById(&#39;iterInput&#39;);
  var iterValEl=document.getElementById(&#39;iterVal&#39;);
  var statusC=document.getElementById(&#39;statusC&#39;);
  var statusRes=document.getElementById(&#39;statusRes&#39;);
  var statusIter=document.getElementById(&#39;statusIter&#39;);
  var statusTime=document.getElementById(&#39;statusTime&#39;);
  var hoverInfo=document.getElementById(&#39;hoverInfo&#39;);

  var timer=null;

  function hslToRgb(h,s,l){
    h=((h%360)+360)%360; s/=100; l/=100;
    var k=function(n){return (n+h/30)%12;};
    var a=s*Math.min(l,1-l);
    var f=function(n){return l-a*Math.max(-1,Math.min(k(n)-3,Math.min(9-k(n),1)));};
    return [Math.round(f(0)*255),Math.round(f(8)*255),Math.round(f(4)*255)];
  }

  function currentC(){
    return {re:parseFloat(reSlider.value), im:parseFloat(imSlider.value)};
  }
  function currentRes(){ return Math.min(2000, Math.max(128, parseInt(resInput.value)||800)); }
  function currentIter(){ return parseInt(iterInput.value)||180; }

  function render(){
    var c=currentC(), res=currentRes(), maxIter=currentIter();
    var t0=performance.now();
    cv.width=res; cv.height=res;
    var img=ctx.createImageData(res,res);
    var data=img.data;
    var step=(YMAX-YMIN)/res;
    var o=0;
    for(var py=0; py<res; py++){
      var y=YMAX-(py+0.5)*step;
      for(var px=0; px<res; px++){
        var x=XMIN+(px+0.5)*step;
        var zx=x, zy=y, i=0, zx2=0, zy2=0;
        while(i<maxIter){
          zx2=zx*zx; zy2=zy*zy;
          if(zx2+zy2>4) break;
          zy=2*zx*zy+c.im;
          zx=zx2-zy2+c.re;
          i++;
        }
        if(i>=maxIter){
          data[o++]=6; data[o++]=10; data[o++]=26; data[o++]=255;
        }else{
          var mag=Math.sqrt(zx2+zy2);
          var nu=Math.log(Math.log(mag)/Math.LN2)/Math.LN2;
          var sm=i+1-nu;
          var hue=((sm*52)+200)%360;
          var rgb=hslToRgb(hue,95,62-Math.min(36,sm*1.4));
          data[o++]=rgb[0]; data[o++]=rgb[1]; data[o++]=rgb[2]; data[o++]=255;
        }
      }
    }
    ctx.putImageData(img,0,0);
    var ms=(performance.now()-t0).toFixed(1);
    statusTime.textCOntent=ms+&#39; ms&#39;;
    statusC.textCOntent=c.re.toFixed(3)+&#39; + &#39;+c.im.toFixed(3)+&#39;i&#39;;
    statusRes.textCOntent=res+&#39;×&#39;+res;
    statusIter.textCOntent=maxIter;
  }

  function schedule(){
    if(timer) clearTimeout(timer);
    timer=setTimeout(function(){
      render();
      syncLabels();
    },90);
  }
  function syncLabels(){
    reValEl.textCOntent=(+reSlider.value).toFixed(3);
    imValEl.textCOntent=(+imSlider.value).toFixed(3);
    resValEl.textCOntent=currentRes();
    iterValEl.textCOntent=currentIter();
  }

  reSlider.addEventListener(&#39;input&#39;,function(){ reValEl.textCOntent=(+reSlider.value).toFixed(3); schedule(); });
  imSlider.addEventListener(&#39;input&#39;,function(){ imValEl.textCOntent=(+imSlider.value).toFixed(3); schedule(); });
  iterInput.addEventListener(&#39;input&#39;,function(){ iterValEl.textCOntent=iterInput.value; schedule(); });
  resInput.addEventListener(&#39;input&#39;,function(){ resValEl.textCOntent=currentRes(); });
  resInput.addEventListener(&#39;change&#39;,render);

  var presets=document.querySelectorAll(&#39;.presets span&#39;);
  presets.forEach(function(p){
    p.addEventListener(&#39;click&#39;,function(){
      reSlider.value=p.getAttribute(&#39;data-re&#39;);
      imSlider.value=p.getAttribute(&#39;data-im&#39;);
      syncLabels();
      render();
    });
  });

  cv.addEventListener(&#39;mousemove&#39;,function(e){
    var rect=cv.getBoundingClientRect();
    var rx=(e.clientX-rect.left)*(cv.width/rect.width);
    var ry=(e.clientY-rect.top)*(cv.height/rect.height);
    var x=XMIN+(rx+0.5)*( (XMAX-XMIN)/cv.width );
    var y=YMAX-(ry+0.5)*( (YMAX-YMIN)/cv.height );
    var c=currentC(), maxIter=currentIter();
    var zx=x, zy=y, i=0;
    while(i<maxIter){
      var zx2=zx*zx, zy2=zy*zy;
      if(zx2+zy2>4) break;
      zy=2*zx*zy+c.im;
      zx=zx2-zy2+c.re;
      i++;
    }
    hoverInfo.textCOntent=&#39;复坐标 &#39;+x.toFixed(4)+(y>=0?&#39; + &#39;:&#39; - &#39;)+Math.abs(y).toFixed(4)+&#39;i · 逃逸迭代 &#39;+i+(i>=maxIter?&#39;(集合内)&#39;:&#39;&#39;);
  });

  var qs=new URLSearchParams(window.location.search);
  var qre=parseFloat(qs.get(&#39;re&#39;)), qim=parseFloat(qs.get(&#39;im&#39;));
  if(isFinite(qre)&&qre>=-2&&qre<=2) reSlider.value=qre;
  if(isFinite(qim)&&qim>=-2&&qim<=2) imSlider.value=qim;
  syncLabels();
  render();
})();
</script>
</body>
</html>

举报
举报主题
相关跟贴

回复