aardio 文档

aardio 范例: p5.js 星空涡旋

import win.ui;
/*DSG{{*/
var winform = win.form(text="p5.js 星空涡旋";right=900;bottom=600;bgcolor=0x000000)
winform.add(
btnChangeColor={cls="button";text="改变配色";left=133;top=16;right=229;bottom=42;dl=1;dt=1;z=3};
btnChangeMode={cls="button";text="切换模式";left=24;top=16;right=120;bottom=42;dl=1;dt=1;z=2};
btnScreenshot={cls="button";text="截图";left=241;top=16;right=313;bottom=42;dl=1;dt=1;z=4};
custom={cls="custom";text="自定义控件";left=6;top=59;right=896;bottom=595;bgcolor=0x000000;db=1;dl=1;dr=1;dt=1;z=1};
tbSpeed={cls="plus";left=345;top=22;right=667;bottom=37;bgcolor=0xFFD9AB23;border={radius=-1};color=0x005CFF;dl=1;dt=1;foreRight=15;forecolor=0xFF1C77FF;paddingBottom=5;paddingTop=5;z=5}
)
/*}}*/

import web.view;
import inet.urlData;

var wb = web.view(winform.custom);

// 网页 JS 通过 window.aardio 调用 wb.external 提供的 aardio 函数
wb.external = {
    /*
    saveImageUrl = function(dataUrl){  
        var path = "/starfield_" + time().format("%Y%m%d_%H%M%S") + ".png";

        //将 web.view 网页传过来的图像 Data URL 保存为本地文件
        inet.urlData.save(path, dataUrl); 
        raw.explore(path);
    };
    */
    saveImageBytes = function(jsByteArray){
         var path = "/starfield_" + time().format("%Y%m%d_%H%M%S") + ".png";

        //直接将 JS 数组转换为 buffer(存储二进制字节串的缓冲区)
        var buf = raw.buffer({BYTE data[]=jsByteArray})
        string.save(path,buf)
        raw.explore(path);

        winform.btnScreenshot.disabledText = null;
    };
}

wb.html = /********
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <style type="text/css">
        * { margin: 0; padding: 0; }
        html, body { 
            height: 100%; 
            overflow: hidden;
            background: #000;
        }
        #info {
            position: fixed;
            bottom: 10px;
            right: 10px;
            color: rgba(255,255,255,0.7);
            font: 11px monospace;
            text-align: right;
            pointer-events: none;
            text-shadow: 0 0 3px rgba(0,0,0,0.8);
        }
    </style>
    <script src="https://registry.npmmirror.com/p5/2.0.5/files/lib/p5.min.js"></script>
</head>
<body>
    <div id="info"></div>

    <script>
    // 配置参数
    let stars = [];
    let spirals = [];
    let mode = 0; // 0:涡旋 1:星河 2:隧道 3:花火
    let speed = 1;
    let colorScheme = 0;
    let centerX, centerY;
    let time = 0;

    // 配色方案
    const colorSchemes = [
        { name: '极光', colors: [[0,255,200], [0,150,255], [150,0,255], [255,0,150]] },
        { name: '火焰', colors: [[255,100,0], [255,200,0], [255,50,50], [200,0,0]] },
        { name: '海洋', colors: [[0,100,200], [0,200,255], [100,255,255], [0,50,150]] },
        { name: '糖果', colors: [[255,100,200], [100,255,200], [255,255,100], [200,100,255]] },
        { name: '星云', colors: [[150,100,255], [255,100,150], [100,150,255], [255,200,100]] }
    ];

    // 星星类
    class Star {
        constructor() {
            this.reset();
            this.z = random(1000);
        }

        reset() {
            this.angle = random(TWO_PI);
            this.radius = random(50, min(width, height)/2);
            this.z = 1000;
            this.pz = this.z;
            this.size = random(1, 3);
            this.speed = random(0.5, 2);
            this.colorIndex = floor(random(4));
        }

        update() {
            this.pz = this.z;
            this.z -= speed * this.speed * 10;

            if (mode === 0) { // 涡旋模式
                this.angle += 0.01 * speed;
                this.radius *= 0.995;
            } else if (mode === 1) { // 星河模式
                this.angle += 0.005 * speed;
                this.radius += speed * 0.5;
            } else if (mode === 2) { // 隧道模式
                this.angle += 0.02 * speed;
            } else if (mode === 3) { // 花火模式
                this.radius += speed * this.speed * 2;
                this.angle += 0.01 * this.speed;
            }

            if (this.z < 1 || this.radius > max(width, height)) {
                this.reset();
            }
        }

        show() {
            fill(255);
            noStroke();

            let sx = map(this.radius * cos(this.angle) / this.z, 0, 1, 0, width);
            let sy = map(this.radius * sin(this.angle) / this.z, 0, 1, 0, height);
            let px = map(this.radius * cos(this.angle) / this.pz, 0, 1, 0, width);
            let py = map(this.radius * sin(this.angle) / this.pz, 0, 1, 0, height);

            sx = centerX + sx;
            sy = centerY + sy;
            px = centerX + px;
            py = centerY + py;

            let r = map(this.z, 0, 1000, 8, 0) * this.size;
            let colors = colorSchemes[colorScheme].colors[this.colorIndex];
            let alpha = map(this.z, 0, 1000, 255, 0);

            push();
            drawingContext.shadowBlur = 20;
            drawingContext.shadowColor = `rgba(${colors[0]},${colors[1]},${colors[2]},0.8)`;
            fill(colors[0], colors[1], colors[2], alpha);
            circle(sx, sy, r);
            pop();

            strokeWeight(r/2);
            stroke(colors[0], colors[1], colors[2], alpha/2);
            line(px, py, sx, sy);
        }
    }

    // 螺旋粒子类
    class Spiral {
        constructor(startAngle) {
            this.angle = startAngle;
            this.radius = 0;
            this.life = 255;
            this.colorIndex = floor(random(4));
        }

        update() {
            this.angle += 0.05 * speed;
            this.radius += 2 * speed;
            this.life -= speed * 2;
        }

        show() {
            if (this.life <= 0) return;

            let x = centerX + this.radius * cos(this.angle);
            let y = centerY + this.radius * sin(this.angle);
            let colors = colorSchemes[colorScheme].colors[this.colorIndex];

            push();
            noStroke();
            fill(colors[0], colors[1], colors[2], this.life);

            // 绘制粒子
            for (let i = 0; i < 5; i++) {
                let offsetAngle = this.angle - i * 0.1;
                let offsetRadius = this.radius - i * 10;
                if (offsetRadius > 0) {
                    let ox = centerX + offsetRadius * cos(offsetAngle);
                    let oy = centerY + offsetRadius * sin(offsetAngle);
                    let size = map(i, 0, 5, 5, 1);
                    circle(ox, oy, size);
                }
            }
            pop();
        }

        isDead() {
            return this.life <= 0 || this.radius > max(width, height);
        }
    }

    function setup() {
        window.canvas = createCanvas(windowWidth, windowHeight);
        centerX = width / 2;
        centerY = height / 2;

        // 创建星星
        for (let i = 0; i < 500; i++) {
            stars.push(new Star());
        }
    }

    function draw() {
        if (mode === 2) {
            for(let i = 0; i <= height; i++) {
                let inter = map(i, 0, height, 0, 1);
                let c = lerpColor(color(0, 0, 20), color(0, 0, 0), inter);
                stroke(c);
                line(0, i, width, i);
            }
        } else {
            background(0, 0, 0, 25);
        }

        push();
        noStroke();
        let colors = colorSchemes[colorScheme].colors[0];
        for (let i = 5; i > 0; i--) {
            fill(colors[0], colors[1], colors[2], 5);
            circle(centerX, centerY, i * 50);
        }
        pop();

        for (let star of stars) {
            star.update();
            star.show();
        }

        for (let i = spirals.length - 1; i >= 0; i--) {
            spirals[i].update();
            spirals[i].show();
            if (spirals[i].isDead()) {
                spirals.splice(i, 1);
            }
        }

        if (frameCount % 5 === 0 && mode !== 2) {
            spirals.push(new Spiral(random(TWO_PI)));
        }

        document.getElementById('info').innerHTML = 
            `模式: ${['涡旋', '星河', '隧道', '花火'][mode]}<br>` +
            `配色: ${colorSchemes[colorScheme].name}<br>` +
            `速度: ${speed.toFixed(1)}x<br>` +
            `FPS: ${Math.round(frameRate())}`;

        time += 0.01;
    }

    function mouseMoved() {
        if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
            centerX = lerp(centerX, mouseX, 0.05);
            centerY = lerp(centerY, mouseY, 0.05);
        }
    }

    function mousePressed() {
        for (let i = 0; i < 20; i++) {
            spirals.push(new Spiral(random(TWO_PI)));
        }
    }

    function windowResized() {
        resizeCanvas(windowWidth, windowHeight);
        centerX = width / 2;
        centerY = height / 2;
    }

    // 供 aardio 调用的函数
    window.changeMode = function() {
        mode = (mode + 1) % 4;
        // 重置星星
        for (let star of stars) {
            star.reset();
        }
    }

    window.changeColor = function() {
        colorScheme = (colorScheme + 1) % colorSchemes.length;
    }

    window.setSpeed = function(value) {
        speed = value;
    }

    window.screenshot = function() { 
        let canvas = window.canvas.canvas;
        // let dataUrl = window.canvas.canvas.toDataURL(); 
        // aardio.saveImageUrl(dataUrl); 

        canvas.toBlob(function(blob) {
            // 转换 Blob 为 Uint8Array
            const reader = new FileReader();

            reader.onload = function() {
                const uint8Array = new Uint8Array(reader.result);
                var byteArray = Array.from( uint8Array )
                aardio.saveImageBytes( byteArray );
            };

            reader.readAsArrayBuffer(blob);
        }, 'image/png'); 

    }
    </script>
</body>
</html>
********/

// 按钮事件
winform.btnChangeMode.oncommand = function(id,event){
    wb.invoke("changeMode");
}

winform.btnChangeColor.oncommand = function(id,event){
    wb.invoke("changeColor");
}

winform.btnScreenshot.oncommand = function(id,event){
    winform.btnScreenshot.disabledText = ["✶","✸","✹","✺","✹","✷"]
    wb.invoke("screenshot");
}

winform.tbSpeed.setTrackbarRange(1,100);
winform.tbSpeed.progressPos = 50;
winform.tbSpeed.onPosChanged = function( pos,triggeredByUser ){

    var speedValue = 0.1 + (pos - 1) * (3.0 - 0.1) / 99;
    wb.invoke("setSpeed", speedValue);
}

winform.tbSpeed.skin({
    background={
        default=0xFF23ABD9
    };
    foreground={
        default=0xFFFF771C;
        hover=0xFFFF6600
    };
    color={
        default=0xFFFF5C00;
        hover=0xFFFF6600
    }
})

winform.show();
win.loopMessage();
Markdown 格式