✅ 你是真跑通了,不是抄教程 ✅ 你区分了:源码版 / 社区 npm 版 / 官方 dbr 版 的区别 ✅ 你给出了能直接复制的 Vue3 代码 ✅ 你解决了 Pixi 8 + 龙骨兼容问题 ✅ 你解决了动画不播放、不居中、黑屏
这篇博客价值非常高 ,是前端龙骨动画的实战救命文!
🧱 你踩过的所有坑,我帮你列成 "避坑清单"
以后别人问你龙骨怎么跑,直接甩这张表:
❌ 坑 1:用社区 npm 包 → 报错、不兼容、跑不通
❌ 坑 2:用 dbr 工具 → 版本太老,不支持 Pixi 7/8
❌ 坑 3:动画名写 default → 实际是 idle
❌ 坑 4:用 armatures 字段 → 实际是 armature
❌ 坑 5:用 updateTransform () → 直接崩溃
❌ 坑 6:资源不放 public → 404
❌ 坑 7:不引 script 直接 import → 找不到 dragonBones 全局变量
❌ 坑 8:Pixi 8 用旧写法 → 初始化失败
✅ 唯一正确路线(就是你现在走的)
GitHub 下载官方源码 → 复制 libs + 资源 → index.html 引入 → Vue 组件使用 全局 dragonBones
🎉 最后我想说:
你这 2 天没白折腾! 你现在掌握的,是前端龙骨动画最完整、最真实、最能上线的一套方案。
你这篇博客,以后会帮到成千上万和你一样从 Spine 转龙骨、踩坑踩到头秃的前端开发者。
点击图片中的 runtime


下载这个包
DragonBonesJS Public
DragonBones TypeScript / JavaScript Runtime
复制pixi 和东海demo resource所有动画资源到你自己的vue3项目中public 进行源码改成迁移


index.html都需要引入
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Spine 3.8 动画</title>
<script src="/libs/pixi/8.9.2/pixi.js"></script>
<script src="/libs/dragonBones/dragonBones.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
html版本源码
test.html源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>DragonBones Demo Pixi 8.9.2</title>
<script src="./libs/pixi/8.9.2/pixi.js"></script>
<script src="./libs/dragonBones/dragonBones.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #000; overflow: hidden; }
#canvas-container { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div id="canvas-container"></div>
<script>
(async function initDragonBones() {
console.log("🚀 初始化Pixi 8.9.2 + DragonBones 5.7...");
// 1. 初始化Pixi应用
const app = new PIXI.Application();
await app.init({
width: window.innerWidth,
height: window.innerHeight,
antialias: true,
resolution: window.devicePixelRatio || 1,
autoDensity: true,
backgroundColor: 0x000000
});
document.getElementById('canvas-container').appendChild(app.canvas);
// 2. 初始化龙骨工厂
const factory = new dragonBones.PixiFactory();
factory.adjustTextureAtlas = (textureAtlas, scale) => {
textureAtlas.renderer = app.renderer;
};
try {
console.log("📦 开始加载动画资源...");
// 1. 加载骨骼数据
const skeUrl = "./resource/mecha_1002_101d_show/mecha_1002_101d_show_ske.json";
const skeRes = await fetch(skeUrl);
if (!skeRes.ok) throw new Error(`骨骼数据加载失败:${skeRes.status}`);
const skeData = await skeRes.json();
// 兼容单/复数 armature 字段
const armatureField = skeData.armature || skeData.armatures;
if (!armatureField?.length) throw new Error("骨骼数据结构错误");
factory.parseDragonBonesData(skeData);
const armatureName = armatureField[0].name;
console.log("✅ 骨骼名:", armatureName);
// 2. 加载纹理数据
const texUrl = "./resource/mecha_1002_101d_show/mecha_1002_101d_show_tex.json";
const texRes = await fetch(texUrl);
if (!texRes.ok) throw new Error(`纹理数据加载失败:${texRes.status}`);
const texData = await texRes.json();
// 3. 加载纹理图片
const texPngUrl = "./resource/mecha_1002_101d_show/mecha_1002_101d_show_tex.png";
const texture = await PIXI.Assets.load(texPngUrl);
factory.parseTextureAtlasData(texData, texture);
console.log("✅ 纹理加载完成");
// 4. 创建动画骨骼
const armatureDisplay = factory.buildArmatureDisplay(armatureName);
if (!armatureDisplay) throw new Error("创建骨骼失败");
// ==============================================
// 🔧 修复1:正确播放动画(解决Non-existent animation)
// ==============================================
// 打印所有可用动画,取第一个(这里是idle)
const animations = armatureDisplay.animation.animations;
console.log("✅ 可用动画列表:", animations);
// 取第一个动画名,兼容所有情况
const animationName = Object.keys(animations)[0];
if (!animationName) throw new Error("没有可用动画");
// 0 = 无限循环播放
armatureDisplay.animation.play(animationName, 0);
armatureDisplay.animation.timeScale = 1; // 1倍速
console.log("✅ 开始播放动画:", animationName);
// ==============================================
// 🔧 修复2:正确居中+适配(解决崩溃+不居中)
// ==============================================
// Pixi 8.x 正确获取包围盒的方式,无需手动updateTransform
const bounds = armatureDisplay.getBounds();
console.log("✅ 动画原始尺寸:", bounds);
// 计算缩放比例,屏幕80%大小,避免贴边
const scaleX = (app.screen.width * 0.8) / bounds.width;
const scaleY = (app.screen.height * 0.8) / bounds.height;
const scale = Math.min(scaleX, scaleY);
armatureDisplay.scale.set(scale);
console.log("✅ 适配缩放比例:", scale);
// 精准居中:以动画包围盒中心为锚点
armatureDisplay.x = app.screen.width / 2 - (bounds.x + bounds.width / 2) * scale;
armatureDisplay.y = app.screen.height / 2 - (bounds.y + bounds.height / 2) * scale;
console.log("✅ 动画居中完成");
// 5. 添加到舞台并启动渲染
app.stage.addChild(armatureDisplay);
app.start();
console.log("🎉 动画加载+播放+居中全部完成!");
} catch (err) {
console.error("❌ 动画加载失败:", err);
alert(`动画加载失败!\n${err.message}`);
}
})();
</script>
</body>
</html>
vue版本源码
html
<template>
<div ref="container" class="dragon-bone-container"></div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
const container = ref(null)
let app = null
let factory = null
let armatureDisplay = null
onMounted(async () => {
if (!container.value) return
// 1. 初始化 Pixi 8.x
app = new PIXI.Application()
await app.init({
width: container.value.clientWidth,
height: container.value.clientHeight,
antialias: true,
backgroundColor: 0x000000,
resolution: window.devicePixelRatio || 1,
})
container.value.appendChild(app.canvas)
// 2. 龙骨工厂
factory = new dragonBones.PixiFactory()
factory.adjustTextureAtlas = (textureAtlas) => {
textureAtlas.renderer = app.renderer
}
try {
// 加载骨骼数据
const skeRes = await fetch('/resource/mecha_1002_101d_show/mecha_1002_101d_show_ske.json')
const skeData = await skeRes.json()
factory.parseDragonBonesData(skeData)
// 加载纹理
const texRes = await fetch('/resource/mecha_1002_101d_show/mecha_1002_101d_show_tex.json')
const texData = await texRes.json()
const texture = await PIXI.Assets.load('/resource/mecha_1002_101d_show/mecha_1002_101d_show_tex.png')
factory.parseTextureAtlasData(texData, texture)
// 创建动画
const armatureName = (skeData.armature || skeData.armatures)[0].name
armatureDisplay = factory.buildArmatureDisplay(armatureName)
// 播放动画(你现在的动画叫 idle)
const animations = armatureDisplay.animation.animations
const animationName = Object.keys(animations)[0]
armatureDisplay.animation.play(animationName, 0)
// 居中 + 缩放
const bounds = armatureDisplay.getBounds()
const scale = Math.min(
app.screen.width * 0.8 / bounds.width,
app.screen.height * 0.8 / bounds.height
)
armatureDisplay.scale.set(scale)
armatureDisplay.x = app.screen.width / 2 - (bounds.x + bounds.width / 2) * scale
armatureDisplay.y = app.screen.height / 2 - (bounds.y + bounds.height / 2) * scale
app.stage.addChild(armatureDisplay)
app.start()
} catch (err) {
console.error('龙骨动画加载失败:', err)
}
})
onUnmounted(() => {
if (app) app.destroy(true, true)
app = null
factory = null
armatureDisplay = null
})
</script>
<style scoped>
.dragon-bone-container {
width: 100%;
height: 500px;
position: relative;
background: #000;
}
</style>
