Flash 默認固定幀率 (fps) 全局生效,無法單獨控制某個 MovieClip 的播放速度。本文實現一套**針對單一 MovieClip、基於滑鼠距離動態調整播放幀率倍數**的系統,核心思路:**在 onEnterFrame 中按倍數跳幀**,將「空間距離」映射為「時間流速」。
playRateCtrfunction playRateCtr(movie:MovieClip, rate:Number):Void {
// 參數防禦
if (rate == undefined) rate = 1;
if (movie == undefined) movie = this;
var cFrame:Number = movie._currentframe;
var nFrame:Number = cFrame + rate; // 核心:當前幀 + 倍數 = 目標幀
movie.gotoAndPlay(nFrame);
}
原理: gotoAndPlay(nFrame) 直接跳轉到目標幀並繼續播放。
• rate=1:正常播放 (每幀前進 1)。
• rate=3:三倍速 (每幀跳過 2 幀,實際播放幀率 3×)。
• rate=0.5:半速 (需配合邏輯處理小數幀,或改用 setInterval 控制)。
• rate<0:倒放。
DtoRatefunction DtoRate(movie:MovieClip, rate_max:Number, tran:Number):Number {
if (movie == undefined) movie = this;
if (rate_max == undefined) rate_max = 3;
if (tran == undefined) tran = 50;
var dx:Number = movie._xmouse; // 滑鼠相對 movie 中心的 x 距離
var dy:Number = movie._ymouse; // y 距離
var dist:Number = Math.sqrt(dx*dx + dy*dy); // 歐氏距離
var dr:Number = Math.min(rate_max, Math.floor(dist / tran)); // 距離轉倍數,封頂
return (rate_max - dr + 1); // 距離越近 → dr 越小 → 返回值越大 → 播放越快
}
映射邏輯:
• 滑鼠在 MovieClip 中心 (dist=0) → dr=0 → 返回 rate_max + 1 (最快)。
• 滑鼠遠離 → dr 增大 → 返回值遞減。
• dist >= rate_max * tran → dr = rate_max → 返回 1 (正常速度)。
• tran 為「距離單位」,控制衰減曲線陡峭度。
// 假設箭頭動畫 MC 實例名 logo_part_1,共 192 幀
logo_part_1.onEnterFrame = function() {
var rate:Number = _root.DtoRate(this, 15, 35); // 最大15倍速,距離單位35像素
_root.playRateCtr(this, rate);
};
效果: 滑鼠靠近箭頭中心 → 箭頭狂轉 (最高 15×);滑鼠遠離 → 緩緩轉動 (1×);滑鼠移出區域 → 恢復正常。極具互動趣味性。
直接 gotoAndPlay 跳幀會導致畫面突變。改用「目標幀 + 緩動插值」:
var targetFrame:Number = movie._currentframe; // 目標幀 (浮點)
movie.onEnterFrame = function() {
var rate = _root.DtoRate(this, 15, 35);
targetFrame += rate;
if (targetFrame > movie._totalframes) targetFrame -= movie._totalframes; // 循環
// 緩動追趕當前幀
movie._currentframe += (targetFrame - movie._currentframe) * 0.3;
// 若需精確控制子元件,需改用自定義播放器模式
};
或使用 Tween 引擎 (TweenLite/GTween) 驅動 currentFrame 屬性。
function playRateCtr(movie:MovieClip, rate:Number):Void {
var cFrame:Number = movie._currentframe;
var nFrame:Number = cFrame + rate;
var total:Number = movie._totalframes;
// 循環邏輯
if (nFrame > total) nFrame = nFrame - total;
else if (nFrame < 1) nFrame = total + nFrame; // 倒放循環
movie.gotoAndPlay(nFrame);
}
class SpeedController {
private var targets:Array = [];
private var centerMC:MovieClip;
public function SpeedController(center:MovieClip) {
centerMC = center;
centerMC.onEnterFrame = driveAll;
}
public function addTarget(mc:MovieClip, maxRate:Number, distUnit:Number):Void {
targets.push({mc:mc, maxRate:maxRate, distUnit:distUnit});
}
private function driveAll():Void {
for each (var t:Object in targets) {
var dx:Number = t.mc._xmouse; // 注意:_xmouse 相對各自 MC
var dy:Number = t.mc._ymouse;
var dist:Number = Math.sqrt(dx*dx + dy*dy);
var dr:Number = Math.min(t.maxRate, Math.floor(dist / t.distUnit));
var rate:Number = t.maxRate - dr + 1;
_root.playRateCtr(t.mc, rate);
}
}
}
// 使用
var ctrl:SpeedController = new SpeedController(logo_part_1);
ctrl.addTarget(gear1_mc, 10, 40);
ctrl.addTarget(gear2_mc, 5, 60);
package {
import flash.display.MovieClip;
import flash.events.Event;
import flash.events.MouseEvent;
public class DynamicSpeedMC extends MovieClip {
public var maxRate:Number = 3;
public var distUnit:Number = 50;
public function DynamicSpeedMC() {
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(e:Event):void {
removeEventListener(Event.ADDED_TO_STAGE, onAdded);
addEventListener(Event.ENTER_FRAME, onFrame);
stage.addEventListener(MouseEvent.MOUSE_MOVE, onStageMove); // 全局滑鼠更新
}
private function onFrame(e:Event):void {
var dx:Number = this.mouseX;
var dy:Number = this.mouseY;
var dist:Number = Math.sqrt(dx*dx + dy*dy);
var dr:int = Math.min(maxRate, int(dist / distUnit));
var rate:Number = maxRate - dr + 1;
nextFrameByRate(rate);
}
private function nextFrameByRate(rate:Number):void {
var targetFrame:Number = currentFrame + rate;
var total:int = totalFrames;
if (targetFrame > total) targetFrame -= total;
else if (targetFrame < 1) targetFrame += total;
gotoAndPlay(int(targetFrame));
}
private function onStageMove(e:MouseEvent):void {
// 可選:用舞台滑鼠位置計算距離,需 globalToLocal 轉換
}
}
}
gotoAndPlay 頻繁調用會觸發幀腳本重複執行,**確保關鍵幀無副作用代碼** (如重複 addEventListener、new Object)。SpeedController 驅動。currentFrame 為只讀屬性,**不能直接賦值**,必須用 gotoAndStop/Play 或 play()/stop() 配合 nextFrame()/prevFrame() 循環控制。TouchEvent 替換滑鼠邏輯,距離計算同理。「距離映射倍數 + 幀跳躍執行」雙函數模型,用 20 行代碼實現了**空間交互控制時間流速**的核心體驗。這不只是技巧,更是「交互即編程」思維的縮影:將用戶輸入 (位置) 映射為系統參數 (速度),構建即時反饋回路。 理解這個模型,你就擁有了從「播放速度」延伸到「時間倒流、局部慢動作、音高同步變速」等所有時間操控能力的鑰匙。