av.video 使用指南 | av.audio 使用指南 | 缓动与弹簧
//av - 彩纸、星光与音效:给界面操作一点反馈
//av.video 使用指南: https://www.aardio.com/zh-cn/docs/library/av/video/
//av.audio 使用指南: https://www.aardio.com/zh-cn/docs/library/av/audio/
//缓动与弹簧: https://www.aardio.com/zh-cn/docs/examples/plus/av/motion.html
import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="av - 彩纸、星光与音效";right=599;bottom=319;bgcolor=0xFFFFFF)
winform.add(
btnDone={cls="plus";text="完成任务";left=330;top=194;right=560;bottom=240;bgcolor=0x59C734;border={radius=8};color=0xFFFFFF;font=LOGFONT(h=-16);notify=1;z=4};
btnPlay={cls="plus";text="播放";left=474;top=38;right=560;bottom=68;bgcolor=0xF0F0F0;border={radius=6};color=0x333333;font=LOGFONT(h=-14);notify=1;z=2};
cmbSound={cls="combobox";left=330;top=40;right=466;bottom=66;edge=1;font=LOGFONT(h=-14);items={};mode="dropdownlist";z=1};
code1={cls="bkplus";text='av.audio.sfx(名称) \u2192 sys.soundPool';left=40;top=56;right=320;bottom=76;align="left";color=0x8A8A8A;font=LOGFONT(h=-13;name='Consolas');z=6};
code2={cls="bkplus";text='create("sparkle") + sfx("sparkle")';left=40;top=140;right=320;bottom=160;align="left";color=0x8A8A8A;font=LOGFONT(h=-13;name='Consolas');z=8};
code3={cls="bkplus";text='create("confetti") + sfx("success")';left=40;top=220;right=320;bottom=240;align="left";color=0x8A8A8A;font=LOGFONT(h=-13;name='Consolas');z=10};
line1={cls="bkplus";left=40;top=100;right=560;bottom=101;bgcolor=0xEEEEEE;z=11};
line2={cls="bkplus";left=40;top=180;right=560;bottom=181;bgcolor=0xEEEEEE;z=12};
note={cls="bkplus";text="粒子按时间直接求值,音效由代码现场合成,不需要任何素材文件。";left=40;top=270;right=560;bottom=294;align="left";color=0x9A9A9A;font=LOGFONT(h=-13);z=13};
star={cls="plus";left=330;top=112;right=378;bottom=160;iconStyle={font=LOGFONT(h=-30;name='FontAwesome')};iconText='\uF006';notify=1;z=3};
starTip={cls="bkplus";text="点击收藏,再点取消";left=396;top=125;right=560;bottom=147;align="left";color=0x8A8A8A;font=LOGFONT(h=-14);z=14};
title1={cls="bkplus";text="试听音效";left=40;top=30;right=300;bottom=54;align="left";color=0x333333;font=LOGFONT(h=-16;weight=700);z=5};
title2={cls="bkplus";text="收藏";left=40;top=114;right=300;bottom=138;align="left";color=0x333333;font=LOGFONT(h=-16;weight=700);z=7};
title3={cls="bkplus";text="完成任务";left=40;top=194;right=300;bottom=218;align="left";color=0x333333;font=LOGFONT(h=-16;weight=700);z=9}
)
/*}}*/
import sys.soundPool;
import time.performance;
import av.audio.sfx;
import av.video.particles;
var particles = av.video.particles;
var $ = av.video;
//---- 音效:启动时生成(每个约 30~100 毫秒)放进音效池,之后一句 play 播放,几个声音可以同时响 ----
var sounds = sys.soundPool(); //没有声卡时为静音模式,play 什么也不做
sounds.add("done",av.audio.sfx("success").trim(-60)); //trim 去掉混响尾部的静音;音量可用 opt.gain(dB)调整
sounds.add("star",av.audio.sfx("sparkle",{dur=0.8}).trim(-60));
sounds.add("unstar",av.audio.sfx("click").trim(-60));
//---- 粒子特效层:铺满客户区的透明画布,粒子可以飞过所有控件 ----
winform.add(fx={cls="canvas";left=0;top=0;marginRight=0;marginBottom=0;anchors={left=1;top=1;right=1;bottom=1}});
winform.fx.setRect( winform.fx.getRect().inflate(100,100) ); //动画可不受窗口限制,orphanWindow 的福利
winform.fx.orphanWindow(true); //转为分层透明窗口,盖在窗体上并跟随窗体移动、缩放
winform.fx.modifyStyleEx(,0x20/*_WS_EX_TRANSPARENT*/); //鼠标穿透到下面的控件
var bursts = {};
//在控件中心爆发。opt 是 particles.create 的参数:scale 按 1080p 视频设计,界面里要缩小并乘以 DPI
var burst = function(kind,ctrl,opt){
var offset = winform.dpiScale(100); //winform.fx 放大后偏移
var x,y,cx,cy = ctrl.getPos(); //相对窗口客户区,与特效层坐标一致
opt.seed = math.random(1,1000000); //每次换个种子,样子不重复
table.push(bursts,{
ps = particles.create(kind,opt);
x = x + cx / 2 + offset;
y = y + cy / 2 +offset;
start = time.performance.tick() //高精度毫秒,画布每帧都按真实时间求值
});
if(!winform.fx.animationId) winform.fx.startAnimation(10);
}
winform.fx.onDrawForeground = function(graphics,rc){
var now = time.performance.tick();
for(i=#bursts;1;-1){
var b = bursts[i];
var t = (now - b.start) / 1000; //爆发后的秒数,粒子位置按 t 直接求值
if(t > b.ps.life) table.remove(bursts,i);
else particles.draw(graphics,b.ps,t,b.x,b.y);
}
}
winform.fx.onAnimation = function(){
if(#bursts) return true; //粒子全部结束就停止动画
}
//---- 完成任务:彩纸 + 成功音效 ----
winform.btnDone.skin({
background = {default=0xFF34C759;hover=0xFF4CD06D;active=0xFF2AA84A};
color = {default=0xFFFFFFFF};
})
winform.btnDone.oncommand = function(){
burst("confetti",winform.btnDone,{
scale = winform.dpiScale(0.6);
colors = [0xFFFF5F6D,0xFF3BA7FF,0xFFFFC233,0xFF34C759,0xFFA66BFF]; //白底上不要用白色
});
sounds.play("done");
}
//---- 收藏:星星弹出 + 星光 + 音效 ----
winform.star.skin({
iconColor = {default=0xFFB8B8B8;hover=0xFFFFB300};
checked = {iconText='\uF005';iconColor={default=0xFFFFB300}};
})
var starScale = 1;
winform.star.onAnimation = function(prevState,beginning,change,timestamp,duration){
starScale = beginning + change * $.spring(timestamp / 1000,4,0.35);
if(timestamp >= duration) starScale = 1;
owner.scale = starScale;
if(timestamp < duration) return true;
}
var starred = false;
winform.star.onStateChange = function(state){
owner.scale = starScale; //skin 切换状态时会重置 scale,这里恢复动画中的值
//鼠标单击、空格键都会切换 checked,在这里统一响应
if(state.checked == starred) return;
starred = !starred;
if(starred){
owner.startAnimation(10,0.4,0.6,700); //从 0.4 倍弹到 1 倍
burst("sparkle",owner,{
scale = winform.dpiScale(0.7);
count = 16; //小图标周围星星少一些更清爽(默认 30)
radius = winform.dpiScale(40); //星光散开的半径(像素)
ringRadius = winform.dpiScale(30); //冲击环半径,比星光小一圈
colors = [0xFFFFB300,0xFFFF7A00,0xFFFFB300,0xFFFF5F6D]; //浅金色在白底上看不清,掺一点橙红
});
sounds.play("star");
}
else {
sounds.play("unstar");
}
}
//---- 试听全部音效预设(首次试听时生成) ----
var preview = function(){
var name = winform.cmbSound.selText;
if(!sounds.has(name)) sounds.add(name,av.audio.sfx(name).trim(-60));
sounds.play(name);
}
winform.cmbSound.items = av.audio.sfx.names;
winform.cmbSound.selText = "pop";
winform.cmbSound.onSelChange = preview;
winform.btnPlay.oncommand = preview;
winform.btnPlay.skin({background = {default=0xFFF0F0F0;hover=0xFFE6E6E6;active=0xFFDADADA}});
winform.show();
win.loopMessage();
Markdown 格式