跳到主要內容

新案上線, Absolut 校園創作競賽

創意絕對.絕對搖滾. 校園創作競賽



在這次的專案,有三個技術點想跟大家分享(如果有更好的方法,歡迎指教討論喔),
1. 自製網頁的ScrollBar
要寫一個ScrollBar不會很難, 但要配合隨視窗縮放的ScrollBar就不是那麼簡單。
在這兒我使用的是bit-101大叔的 minimalcomps 組件裡的VSlider。
該組件的特色,就是有個setSize的函式, 能讓你動態更改組件大小且不會變型。
基本用法如下:
new VSlider(要加入到那個容器裡, x座標, y座標, change偵聽函式);
決解ScrollBar的程式,再補上偵聽stage的Resize事件, 和偵聽stage的MouseWheel事件
完成(誤)
設計師當然不會用這樣的樣計樣式, 好在該特組件在建立元件時, 會執行addChildren函式
我們只要去override後,加入自己的元件,然後把對映的變數重新指過去就可以了
先在flash做好ScrollBar元件,並取好名稱
在這我不使用flash直接compile,因為太慢, 而是使用Embed把該swf裡的元件重新加進來。
再使用FlexSDK來做compile的動作。
自製ScrollBar的程式碼
/**
* @author milkmidi
* @see http://milkmidi.blogspot
* @version 1.0.1
* @date created 2010/03/24/
*/
package milkmidi.tutorials.absolut.cast {  
import com.bit101.components.VSlider; 
import flash.display.*; 
import flash.events.*;  
[Embed(source='../../../../../assets/Assets.swf',symbol="Scroll_mc")]
public class SliderMC extends VSlider{  
public var thumb_mc     :MovieClip;
public var track_mc     :MovieClip;
private var _addWheelListener:Boolean = false;
public function SliderMC(pParent:DisplayObjectContainer = null, pX:int = 0 , pY:int = 0, pChangeHandler:Function = null)  {      
super(pParent, pX, pY, pChangeHandler);     
}     
override protected function addChildren():void {      
_handle = thumb_mc;
_handle.buttonMode = true;
_handle.addEventListener(MouseEvent.MOUSE_DOWN, onDrag);    
}  
private function _mouseWheelHandler(e:MouseEvent):void {   
if(e.delta>0)
value += 15;
else
value -= 15;     
onSlide(e);   
}
override public function get value():Number { return super.value; }  
override public function set value(value:Number):void {   
super.value = value;
onSlide(null);
}  
public function get addWheelListener():Boolean { return _addWheelListener; }  
public function set addWheelListener(value:Boolean):void {
_addWheelListener = value;
if (_addWheelListener)
stage.addEventListener(MouseEvent.MOUSE_WHEEL , _mouseWheelHandler);   
else
stage.removeEventListener(MouseEvent.MOUSE_WHEEL , _mouseWheelHandler);      
}
override protected function drawBack():void {
track_mc.height = height;
}
override protected function drawHandle():void {   
positionHandle();
}  
} 
}
主程式測試碼
/**
* @author milkmidi
* @see http://milkmidi.blogspot
* @version 1.0.1
* @date created 2010/04/07/
*/
package  {  
import com.bit101.components.HBox;
import com.bit101.components.HSlider;
import com.bit101.components.VSlider;
import flash.display.*;  
import flash.events.Event;
import milkmidi.tutorials.absolut.cast.SliderMC;
public class A01SliderMCDemo extends Sprite {    

private var _scroll:SliderMC;
public function A01SliderMCDemo()  {   
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE , init);
}  

private function init(e:Event=null):void {
removeEventListener(Event.ADDED_TO_STAGE, init);   
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener(Event.RESIZE , _resizeHandler);
_scroll = new SliderMC(this, 0, 0, _slidrChangeHandler);
_scroll.addWheelListener = true;
_scroll.setSliderParams(0, 100, 50); 
_resizeHandler(null);   
//以下是測試用的
new VSlider(this, 0, 0, _slidrChangeHandler);
}

private function _resizeHandler(e:Event):void {
_scroll.x = stage.stageWidth - 40;
_scroll.setSize(200, stage.stageHeight);
}

private function _slidrChangeHandler(e:Event):void{
trace(e.currentTarget.value)
}
} 
}
2. 主視覺要隨視窗大小作定位
在這個案子裡,主視覺有二種模式, 一般模式(要完全罝中)
資訊打開模式(配合第一點的ScrollBar數值來作定位)
我想第一般模式很簡單, 直接對stage做Resize偵聽, 然後就完全置中
而第打開模式,其實也不難,反正ScrollBar裡的value預設就是0到100。
當ScollBar value 在0時, 主視覺物件的y軸也在0,
當ScrollBar value 在100時,主視覺物件的y軸就等於 (主視覺物件的高度-場景高*負1)
(keyVisual.height-stage.stageHeight*-1)
好了,公式出來了,剩下的應該不難。
package  {  

public class A02KeyVisualDemo extends A01SliderMCDemo{  

private var _keyVisual:KeyVisual_mc = new KeyVisual_mc();
public function A02KeyVisualDemo()  {   
addChild(_keyVisual);
}  
override protected function _onScrollChange(pValue:int):void {
pValue = 100 - pValue;
//因為VSlider拉到底是0, 跟我們需要的值是相反的, 所以要倒過來
var _rang:int = (_keyVisual.height - stage.stageHeight) * -1;   
_keyVisual.y = _rang * pValue / 100;         
}
} 
}
3. 效能問題
先直接看效能未優化版(僅供demo用, 活動資訊請以正式網址為主)
你可以看到整個網站在進場播放動畫時,是非常lag, 且整個網站的fps非常的低。
二者有非常明顯的差別(如果你的電腦配備超好的話,應該看不出來)
二個版本用的fla檔都是同一個。
先了解一下flash吃效能的幾個點。
flv:影片播放是很吃效能的,不管是外載還是匯入到flash裡,都非常吃效能,特別在大畫面時更明顯。
決解方法:用檔案k數來換, 把影片轉成序列圖檔
序列圖檔:能用jpg當然是最好的,但要有透明度的話,就一定要用PNG
在使用PNG時要特別注意, 假設你的圖片是600x400, 但真正有圖型的大小可能只有300x200, 其他的部份都是透明色塊, 請記得。打散他, 然後把用不到的透明色塊給delete掉, 這樣可以省下很多不必要的運算(經人體實驗是有效的)。
大畫面物件+多物件移動+序列圖檔:
就像這次的案子遇到的問題一樣,設計師做好美美的動畫,做完發現跑起來很lag,本來是想減少動畫量,但就少了一種感覺, 而我使用的方法就是 BitmapData
嘿嘿,想到了嗎
使用 BitmapData 類別,把該動畫所有的影格,事前畫成BitmapData, 在存在Array裡
當要播放動畫時,就去指定的Array裡把BitmapData拿出來。
這樣不管畫面上有多少物件,都視同只有一個BitmapData, 等到動畫播完時,在把Array裡的BitmapData Dispose掉,這樣動畫也順了,效能也有了。
但使用時還是需要注意幾個點:
一開始要把一個MovieClip裡的全部影格畫成BitmapData, 表示一瞬間會消耗大量的CPU,此時使用者的電腦可能會停頓一下,那可不可以改成邊播MovieClip邊畫呢,答案是這樣做的效能沒什麼差
所以我的作法是把整段動畫分階段去畫,一開始可能先畫20個影格,當動畫播到20個影格時,再去畫另外的20個影格
等到動畫全部播完後,再整個Dispose掉。

package milkmidi.display { 
import flash.display.*; 
import flash.events.Event;
import flash.geom.Matrix;
import flash.net.URLRequest;
import flash.utils.clearInterval;
import flash.utils.clearTimeout;
import flash.utils.setTimeout; 
public class BitmapDataSequence extends Sprite {
public static const END_FRAME:String = "endFrame";
private var _target   :MovieClip;
private var _targetWidth :int;
private var _targetHeight :int;  
private var _fps   :uint;
private var _totalFrames :uint;  
private var _bitmap   :Bitmap = new Bitmap();
private var _bmdArray  :Array = [];    
private var _frame   :int = 1;  
private var _inter   :int;
private var _drawMatrix  :Matrix;
private var _sequenceTime :Number;
public function BitmapDataSequence(pTargetMC:MovieClip, pWidth:Number = 320, pHeight:Number = 240, pFps:Number = 30):void {   
_fps = pFps;      
_targetWidth = pWidth;
_targetHeight = pHeight;   
_target = pTargetMC;         
_totalFrames = _target.totalFrames;
addChild(_bitmap);   
}
public function startSequence(pTime:Number = 0 ):void {   
_sequenceTime = pTime;
convert();   
}  
public function prevFrame():void {
_frame--; 
_frame = _frame < 1 ? 1 :_frame;   
showFrameBitmapData(_frame);   
}
public function nextFrame():void {
_frame++; 
_frame = _frame > _totalFrames  ? _totalFrames : _frame;   
showFrameBitmapData(_frame);
}
public function play():void {
showFrameBitmapData(_frame);   
if (_frame == _totalFrames) {    
dispatchEvent(new Event(END_FRAME));
}
_frame %= _totalFrames;      
_frame++;   
_inter = setTimeout(play, 1000 / _fps);      
}
public function stop():void {
clearTimeout(_inter);
}
public function gotoAndStop(pFrame:uint):void {   
stop();
_frame = pFrame;
showFrameBitmapData(_frame);
}
public function gotoAndPlay(pFrame:uint):void { 
stop();
_frame = pFrame;   
play();
}
private function convert():void {    
_target.gotoAndStop(_frame);   
var _mtr:Matrix = new Matrix();       
var _bmp:BitmapData = new BitmapData(_targetWidth, _targetHeight, true, 0x00000000);
_bmp.draw(_target, _drawMatrix);      
_bmdArray.push(_bmp);
_frame++;  
//trace(_frame, _bmdArray.length);
if (_frame > _totalFrames) {
stop();    
_frame = 1;    
showFrameBitmapData(1);
dispatchEvent(new Event(Event.COMPLETE));
return;
}   
if (_sequenceTime == 0)   
convert();
else
setTimeout( convert, _sequenceTime * 1000);
}  

private function showFrameBitmapData(pFrame:uint):void {
_bitmap.bitmapData = _bmdArray[pFrame - 1];    
}
public function get totalFrames():uint { return _totalFrames; }   
public function get currentFrame():uint { return _frame; }

public function get drawMatrix():Matrix { return _drawMatrix; }  
public function set drawMatrix(value:Matrix):void {
_drawMatrix = value;
}
public function destroy():void {
clearInterval(_inter);
for each (var _bmp:BitmapData in _bmdArray) {
_bmp.dispose();
}        
}
}

}
SourceCodeDownload

留言

jerry寫道…
一個字~強~
匿名表示…
http://ecodazoo.com/
請問老師這個是怎麼做的阿?
匿名表示…
dispatchEvent(new Event('END_FRAME'));
?是這樣嗎?
匿名表示…
動畫全部畫完再dispose
這樣畫面就被清掉了
如果要不斷播放
就不dispose嗎
還是不斷play() > dispose();
那個效能較好
匿名表示…
奶綠老師,不好意思..A02KeyVisualDemo這個在我的認知是在寫-視覺顯示區那個元件,但是我不知道怎麼應用這個as..,主場景程式和SliderMC我已經懂了,但是A02KeyVisualDemo程式我懂,但是不知道怎麼應用且 是用在哪?!


不好意思,我很想學奶綠老師分享的這支程式,所以想問清楚~


如果A02KeyVisualDemo是視覺顯示區那個元件的class,那不用embed該元件嗎?!
milkmidi寫道…
A02KeyVisualDemo extends A01SliderMCDemo了,所以SlidrMC的程式已經有就不用再重寫
而那個主要的視覺元件,我是使用swc
你可以看到Assets.swf是有被加入到FlashDevelop專案裡,且是藍色的
所以我在上方new的KeyVisual_mc就是swc裡的元件
milkmidi寫道…
http://ecodazoo.com/
是日本AS大師roxik自已原創的3D Engine
我只知道大概的方法,但沒辦法做到像他這麼順。

dispatchEvent(new Event('END_FRAME'));
這指的是什麼呢??

動畫全部播就就dispose, 是因為動畫就只播過一次就不要,所以最後才把他整個dispose掉
匿名表示…
奶綠老師 不好意思 我還是不太懂 為什麼 A02KeyVisualDemo extends A01SliderMCDemo 之後 A02KeyVisualDemo 的程式裡 就可以使用Asset.swf的元件?

那換句話說 我可以直接讓視覺元件直接去link A02KeyVisualDemo 嗎?!

不好意思..麻煩您了 我是不太懂swc和embed的方式。
milkmidi寫道…
我來說明一下swc和embed的關系好了
swc:是可以包含設覺元件和程式碼的元件
可以把一堆as檔,包裝成一個swc,
你可以想成是把他zip起來。
怎麼產生swc呢, 只要在發佈面版裡, 將swc選項打勾, 發佈後, 就會幫你產生swc檔, 要注意喔, 只有元件庫裡有勾選成類別的才會匯出在swc裡。
怎麼用呢:FlashCS4,Flex,FlashDevelop都可以直接使用swc元件,只有FlashCS3不能。
在我的A02KeyVisualDemo這隻,我有去new一個KeyVisual_mc類別, 這個類別就是憂在Assets.fla裡製作的。
而Embed是將外部的元素匯入到swf檔,有點像是將檔案匯到flash的元件庫裡。
Embed是由Flex來的, 因為Flex沒有元件庫的概念, 所以才有這個語法可以使用
但要注意一下,使用Embed swf檔時,該swf裡的程式碼都會消失喔。
希望這樣對你有幫助
匿名表示…
嗯! 清楚了~ 謝謝奶綠老師!!
我有寫出來了~ 大開心!
匿名表示…
老師 請問_sequenceTime 這變數的功能是?
milkmidi寫道…
設成0的話
會一開始就把全有的影格的BitmapData劃完
如果影格數很長, 此時會非常吃效能,然後整個停住
加這個變數是用來告訴flash幾毫秒要劃一張。
匿名表示…
感謝奶綠大的分享,但有一個小問題我想請教,以下是我的程式碼。
var seq_bg:BitmapDataSequence=new BitmapDataSequence(designers_bg)
seq_bg.startSequence()
然後我原本設定STOP,場景上的影片他自己會開始跑,但是還是會頓,打開工作管理員,效能也沒有差,請問是我使用方法錯誤嗎?

這個網誌中的熱門文章

webpack2 入門實戰 1

大家好,我是奶綠茶
前端戰場不再只是寫寫 js / css , 各種框架、前處理工具百花齊放
身為前端工程師,不只要把程式寫完,還要寫好
老師說:選對好工具,事情就完成一半
如果你還在一隻 JS 打完全部程式,一隻 css 寫所有的 style
每次存檔還在手動 reload 網頁, 圖片壓 K 壓到不要不要的
透過奶綠伯的系列教學,讓你了解 webpack2 帶來的優勢
學會 webpack 可能不會加薪,但至少可以準時下班(誤)
1. 安裝 nodejs
請參考 gulp 安裝編

2. 安裝 global webpack , 筆者使用的是 2.2.1 版本
npm i webpack@2.2.1 -g
3. 在專案的根目錄放一隻 webpack.config.js
entry:你的主 js 進入點
output.filename:webpack 打包後的檔名
output.path:webpack 打包後的路徑
var path = require('path'); module.exports = { entry: './src/app.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') } };
4. require , module.exports
現在前端都 module 化
可以每個獨立的功能都寫成單一的 js module
除了好管理,也方便讓團隊使用
寫一隻 module_exports_util.js
每隻經過 webpack 打包的 js , 都會是獨立的檔案
所以變數都是私有的, 外部成員都無法得到
在這個 module 裡,我們想開放二個函式
add , getName
所以在最後的 module.exports 指定
筆記加入 jsdoc , 為了方便在開發時,能夠有型別的提示

var name = "milkmidi"; /** * @param {number} num1 * @param {number} num2 * @return {number} */ function ad…

Android完全入門篇-01 安裝SDK與裝置USBDriver

大家好,我是奶綠茶
筆者以 Windows 系統來介紹安裝
1. 下載 Android SDK
http://developer.android.com/sdk/index.html
有分 32 位元和 64 位元的版本
為了教學方便,筆者將檔案放置在 D:\ 下
壓開後會有
eclipse資料夾:Eclipse程式碼編譯器
sdk:Android 開發用的 SDK
SDK Manager.exe:sdk管理用的軟體


2. 開啟手機的"開發人員選項"
部份手機該選項被隱藏起來了
要開啟的話,點選"關於手機"
接著連點"版本號碼"七次,就會重新開啟該功能
接著再開啟"USB偵錯"功能
接上你的 USB 線後
這樣你的電腦才能透過 USB 線來偵錯手機的資訊
3. 為裝置安裝 adb usb driver
因為各家廠牌的 Driver 都不同也不通用,所以需要自行 Google 找 Driver
可以用"XXX廠牌 型號 adb usb driver"關鍵字來找
例:HTC Newone adb usb driver
接著找到 我的電腦 / 內容


裝置管理員

會看到一個金嘆號未知的裝置, 按右鍵更新驅動程式軟體

選擇"瀏覽電腦上的驅動程式軟體"

完成後就會看到裝置啟動,並出現 XXX Android ADB Interface

4.測試是否連接成功
進入到第一步下載的 sdk 資料夾裡
D:\adt-bundle-windows-x86_64-20131030\sdk
然後在 platform-tools 資料夾上按住 Shift 鍵,然後再按滑鼠右鍵
選擇"在此處開啟命令視窗"
(此功能只有 Win7 之後的版本才支援, XP使用者就要自行輸入 dos 指令進入到資料夾)
輸入 adb devices
如果看到一串數字,並顯示 device 就表示連線成功
5.新增環境變數(選擇性設定)
為了方便的使用 adb 指令
可以為 windows 設定變數
開啟我的電腦 / 內容 / 左邊的"進階系統設定"
點選下方 環境變數

下方系統變數,找到 Path的選項,按下編輯

在變數值欄位的最後方加入
;D:\adt-b…

webpack2 入門實戰 3 scss,html,file-loader

大家好,我是奶綠茶
上一篇介紹了 webpack 的核心功能 loader
這篇再來補強各種常用的 loader
css-loader:解悉 css 檔
extract-loader:這個有點難翻,下面會介紹
file-loader:存成實體的檔案, 如圖片
html-loader:解悉 html 檔
sass-loader:解悉 scss 檔
url-loader:解悉圖片路徑
webpack.config.js
module.exports = { resolveLoader: { // 所有用到的 loader, -loader 可以不用打 moduleExtensions: [ "-loader" ], }, resolve: { // 在 require 檔案, 如果不想寫完整的路徑 // 可以加入這些目錄, 讓 webpack 自動尋找對的檔案 // 請注意我們加入了 src/img 路徑,後面會再介紹到 modules: [ path.resolve( 'src/html' ), path.resolve( 'src/img' ), path.resolve( 'src/css' ), path.resolve( 'src/js' ), path.resolve( "node_modules"), ], // 在 require 時可以不用打副檔名 extensions: [ ".js", ".scss" ] }, } 再來就是 loader 的介紹
scss
在這要做多組合的應用
先將 .scss 透過 scss-loader 轉換, 並產生 sourceMap 檔
再過 css-loader
最後再過 style-loader
{ test: /\.scss$/, // 多個 loader 組合, 可寫成一行,或…