利用 AS3 簡易 3D 透視投影(無需 Stage3D),結合三角函數計算圓周分佈、縮比與深度排序,打造滑鼠垂直位置控制旋轉速度的立體旋轉選單。
Menu Item,註冊點中心。menuItemText,嵌入字符(基本拉丁、標點)。Menu Item → 連結/屬性 → 勾選為 ActionScript 導出,類別名 MenuItem,基類 flash.display.Sprite。package {
import flash.display.*;
import flash.events.*;
import flash.geom.*;
import caurina.transitions.Tweener; // 需外部庫,可改用原生 Tween
public class Menu3D extends Sprite {
private const NUM_ITEMS:uint = 20;
private var menuItems:Array = [];
private var focalLength:Number = 350;
private var vanishingPointX:Number = stage.stageWidth / 2;
private var vanishingPointY:Number = stage.stageHeight / 2;
private var angleSpeed:Number = 0;
private var radius:Number = 128;
private var angleDiff:Number = Math.PI * 2 / NUM_ITEMS; // 360° 平均分佈
public function Menu3D() {
// 建立選單項目
for (var i:uint = 0; i < NUM_ITEMS; i++) {
var item:MenuItem = new MenuItem();
item.buttonMode = true;
item.mouseChildren = false;
item.menuItemText.text = "Menu " + i;
var startAngle:Number = angleDiff * i;
item.currentAngle = startAngle;
item.xpos3D = -radius * Math.cos(startAngle) * 0.5;
item.ypos3D = radius * Math.sin(startAngle);
item.zpos3D = radius * Math.cos(startAngle);
updateItemTransform(item);
addChild(item);
menuItems.push(item);
item.addEventListener(MouseEvent.MOUSE_OVER, onOver);
item.addEventListener(MouseEvent.MOUSE_OUT, onOut);
item.addEventListener(MouseEvent.CLICK, onClick);
}
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function updateItemTransform(item:MenuItem):void {
var scaleRatio:Number = focalLength / (focalLength + item.zpos3D);
item.scaleX = item.scaleY = scaleRatio;
item.x = vanishingPointX + item.xpos3D * scaleRatio;
item.y = vanishingPointY + item.ypos3D * scaleRatio;
item.alpha = 0.3 + scaleRatio * 0.7; // 遠淡近實
}
private function onEnterFrame(e:Event):void {
// 滑鼠 Y 控制旋轉速度
angleSpeed = (mouseY - stage.stageHeight / 2) * 0.0002;
for each (var item:MenuItem in menuItems) {
item.currentAngle += angleSpeed;
item.xpos3D = -radius * Math.cos(item.currentAngle) * 0.5;
item.ypos3D = radius * Math.sin(item.currentAngle);
item.zpos3D = radius * Math.cos(item.currentAngle);
updateItemTransform(item);
}
sortZ();
}
private function sortZ():void {
// 依 zpos3D 降序排列(遠在前、近在後),確保遮擋正確
menuItems.sortOn("zpos3D", Array.NUMERIC | Array.DESCENDING);
for (var i:int = 0; i < menuItems.length; i++) {
setChildIndex(menuItems[i], i);
}
}
private function onOver(e:MouseEvent):void {
Tweener.addTween(e.currentTarget, {scaleX:1.2, scaleY:1.2, time:0.3, transition:"easeOutQuad"});
}
private function onOut(e:MouseEvent):void {
Tweener.addTween(e.currentTarget, {scaleX:1, scaleY:1, time:0.3, transition:"easeOutQuad"});
}
private function onClick(e:MouseEvent):void {
trace("Clicked: " + e.currentTarget.menuItemText.text);
}
}
}
scaleRatio = focalLength / (focalLength + z) 模擬透視縮放,x = vpX + x3D * scaleRatio、y = vpY + y3D * scaleRatio 投影至 2D 舞台。xpos3D = -radius * cos(a) * 0.5、zpos3D = radius * cos(a) 形成 XZ 平面橢圓,Y 軸用 sin(a) 產生上下起伏。sortOn("zpos3D", DESCENDING) 並重設 setChildIndex,確保遠處項目被近處遮蓋。angleSpeed = (mouseY - centerY) * 0.0002,滑鼠越偏離中心旋轉越快。代碼使用 caurina.transitions.Tweener(舊版 GreenSock 前身),現代專案建議改用 GSAP 3(gsap.to())或原生 fl.transitions.Tween 移除外部依賴。
此範例展示了純 AS3 實現偽 3D 旋轉選單的核心數學與渲染邏輯,是理解 3D 投影、深度排序、互動控制的優秀練習專案。