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 格式