Created
July 16, 2025 02:58
-
-
Save stevecyj/30da2cf40804d2520036325bbbbc729c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <template> | |
| <div class="winning-animation"> | |
| <canvas ref="canvas" width="512" height="512"></canvas> | |
| <div v-if="loading" class="loading">載入中...</div> | |
| <div v-if="error" class="error">錯誤: {{ error }}</div> | |
| </div> | |
| </template> | |
| <script> | |
| export default { | |
| name: "WinningAnimation", | |
| props: { | |
| autoPlay: { | |
| type: Boolean, | |
| default: true, | |
| }, | |
| loop: { | |
| type: Boolean, | |
| default: true, | |
| }, | |
| }, | |
| data() { | |
| return { | |
| animation: null, | |
| stage: null, | |
| loading: false, | |
| error: null, | |
| isLoaded: false, | |
| loopCheckInterval: null, | |
| } | |
| }, | |
| async mounted() { | |
| await this.initAnimation() | |
| }, | |
| beforeDestroy() { | |
| this.cleanup() | |
| }, | |
| methods: { | |
| async initAnimation() { | |
| try { | |
| this.loading = true | |
| this.error = null | |
| // 載入 CreateJS | |
| await this.loadCreateJS() | |
| // 載入動畫腳本 | |
| await this.loadAnimationScript() | |
| // 設定動畫 | |
| this.setupAnimation() | |
| this.isLoaded = true | |
| this.loading = false | |
| if (this.autoPlay) { | |
| this.play() | |
| } | |
| } catch (error) { | |
| this.error = error.message | |
| this.loading = false | |
| console.error("動畫載入失敗:", error) | |
| } | |
| }, | |
| loadCreateJS() { | |
| return new Promise((resolve, reject) => { | |
| if (window.createjs) { | |
| resolve() | |
| return | |
| } | |
| const script = document.createElement("script") | |
| script.src = "https://code.createjs.com/1.0.0/createjs.min.js" | |
| script.onload = resolve | |
| script.onerror = () => reject(new Error("CreateJS 載入失敗")) | |
| document.head.appendChild(script) | |
| }) | |
| }, | |
| loadAnimationScript() { | |
| return new Promise((resolve, reject) => { | |
| if (window.AdobeAn && window.AdobeAn.getComposition) { | |
| resolve() | |
| return | |
| } | |
| const script = document.createElement("script") | |
| script.src = "/animate/Winning/Winning.js" | |
| script.onload = resolve | |
| script.onerror = () => reject(new Error("動畫腳本載入失敗")) | |
| document.head.appendChild(script) | |
| }) | |
| }, | |
| setupAnimation() { | |
| const canvas = this.$refs.canvas | |
| const comp = window.AdobeAn.getComposition( | |
| "4E951434865B49A08935ABFBCEBAFC1E" | |
| ) | |
| if (!comp) { | |
| throw new Error("找不到動畫組合") | |
| } | |
| const lib = comp.getLibrary() | |
| // 修復資源路徑 | |
| const manifest = lib.properties.manifest.map((item) => ({ | |
| ...item, | |
| src: `/animate/Winning/${item.src}`, | |
| })) | |
| const loader = new window.createjs.LoadQueue(false) | |
| loader.addEventListener("fileload", (evt) => { | |
| const images = comp.getImages() | |
| if (evt && evt.item.type === "image") { | |
| images[evt.item.id] = evt.result | |
| } | |
| }) | |
| loader.addEventListener("complete", (evt) => { | |
| const ss = comp.getSpriteSheet() | |
| const queue = evt.target | |
| const ssMetadata = lib.ssMetadata | |
| // 建立 sprite sheets | |
| for (let i = 0; i < ssMetadata.length; i++) { | |
| ss[ssMetadata[i].name] = new window.createjs.SpriteSheet({ | |
| images: [queue.getResult(ssMetadata[i].name)], | |
| frames: ssMetadata[i].frames, | |
| }) | |
| } | |
| // 建立動畫和舞台 | |
| this.animation = new lib.Winning() | |
| this.stage = new lib.Stage(canvas) | |
| // 設定動畫屬性 | |
| this.animation.loop = this.loop | |
| this.stage.addChild(this.animation) | |
| // 設定基本縮放和位置 | |
| const scale = 1 // 512x512 不需要額外縮放 | |
| this.stage.scaleX = this.stage.scaleY = scale | |
| this.stage.x = (canvas.width - 512 * scale) / 2 | |
| this.stage.y = (canvas.height - 512 * scale) / 2 | |
| // 添加循環播放邏輯 | |
| this.setupAnimationLoop() | |
| // 啟動 ticker | |
| window.createjs.Ticker.framerate = 24 | |
| window.createjs.Ticker.addEventListener("tick", this.stage) | |
| window.AdobeAn.compositionLoaded(lib.properties.id) | |
| }) | |
| loader.addEventListener("error", (evt) => { | |
| throw new Error(`資源載入失敗: ${evt.item?.src || "未知"}`) | |
| }) | |
| loader.loadManifest(manifest) | |
| }, | |
| setupAnimationLoop() { | |
| if (!this.animation || !this.loop) return | |
| // 使用 Ticker 監聽動畫狀態,實現循環播放 | |
| const checkAnimationLoop = () => { | |
| if (this.animation && this.loop) { | |
| // 檢查動畫是否播放完成 (需要根據實際幀數調整) | |
| if (this.animation.currentFrame >= this.animation.totalFrames - 1) { | |
| // 重新播放動畫 | |
| this.animation.gotoAndPlay(0) | |
| } | |
| } | |
| } | |
| // 每隔一定時間檢查動畫狀態 | |
| this.loopCheckInterval = setInterval(checkAnimationLoop, 100) | |
| }, | |
| play() { | |
| if (this.stage && this.animation) { | |
| this.stage.tickEnabled = true | |
| this.animation.gotoAndPlay(0) | |
| // 確保循環播放邏輯正在運行 | |
| if (this.loop && !this.loopCheckInterval) { | |
| this.setupAnimationLoop() | |
| } | |
| } | |
| }, | |
| pause() { | |
| if (this.stage) { | |
| this.stage.tickEnabled = false | |
| } | |
| // 暫停時清理循環檢查 | |
| if (this.loopCheckInterval) { | |
| clearInterval(this.loopCheckInterval) | |
| this.loopCheckInterval = null | |
| } | |
| }, | |
| stop() { | |
| if (this.stage && this.animation) { | |
| this.stage.tickEnabled = false | |
| this.animation.gotoAndStop(0) | |
| } | |
| // 停止時清理循環檢查 | |
| if (this.loopCheckInterval) { | |
| clearInterval(this.loopCheckInterval) | |
| this.loopCheckInterval = null | |
| } | |
| }, | |
| cleanup() { | |
| // 清理循環檢查 interval | |
| if (this.loopCheckInterval) { | |
| clearInterval(this.loopCheckInterval) | |
| this.loopCheckInterval = null | |
| } | |
| if (this.stage && window.createjs) { | |
| window.createjs.Ticker.removeEventListener("tick", this.stage) | |
| this.stage.removeAllChildren() | |
| } | |
| this.stage = null | |
| this.animation = null | |
| }, | |
| }, | |
| } | |
| </script> | |
| <style scoped> | |
| .winning-animation { | |
| position: relative; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| width: 512px; | |
| height: 512px; | |
| } | |
| canvas { | |
| display: block; | |
| } | |
| .loading { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| color: #666; | |
| font-size: 16px; | |
| background: rgba(255, 255, 255, 0.8); | |
| padding: 10px 20px; | |
| border-radius: 4px; | |
| } | |
| .error { | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| color: #c62828; | |
| font-size: 14px; | |
| text-align: center; | |
| padding: 10px; | |
| background: rgba(255, 255, 255, 0.9); | |
| border-radius: 4px; | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment