@versa_ai/vmml-timeline
v1.4.78
Published
如果在 NextJS 项目中使用时遇到 `Module not found: Can't resolve '../build/Release/canvas.node'` 错误,请配置 `next.config.js`:
Readme
@versa_ai/vmml-timeline
NextJS 使用配置
如果在 NextJS 项目中使用时遇到 Module not found: Can't resolve '../build/Release/canvas.node' 错误,请配置 next.config.js:
方案1: 动态导入(推荐)
在你的组件中使用动态导入,避免SSR问题:
import dynamic from 'next/dynamic';
// 动态导入timeline组件,禁用SSR
const VmmlTimeline = dynamic(
() => import('@versa_ai/vmml-timeline').then((mod) => mod.VmmlTimeline),
{
ssr: false,
loading: () => <div>Loading timeline...</div>
}
);
export default function MyPage() {
return (
<div>
<VmmlTimeline
vmml={vmmlData}
// ... 其他props
/>
</div>
);
}方案2: Webpack配置
// next.config.js
module.exports = {
webpack: (config, { isServer }) => {
if (isServer) {
// 服务端排除 fabric,避免canvas.node错误
config.externals = config.externals || [];
config.externals.push('fabric');
}
return config;
}
};重要提醒:如果使用方案2,建议同时使用动态导入来避免hydration错误。
方案3: 完整解决方案(如果上面都不行)
// next.config.js
const webpack = require('webpack');
module.exports = {
webpack: (config, { isServer }) => {
if (isServer) {
config.externals = config.externals || [];
config.externals.push('fabric');
} else {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
encoding: false
};
}
return config;
}
};安装依赖:
npm install fabric
# 或
pnpm add fabricNextJS 使用示例(解决SSR问题)
import { useRef, useState, useEffect } from "react";
import dynamic from "next/dynamic";
const VmmlTimeline = dynamic(
() => import('@versa_ai/vmml-timeline').then((mod) => mod.VmmlTimeline),
{
ssr: false,
loading: () => <div>Loading timeline...</div>
}
);
const Player = ({ vmml }) => {
const [vmmlData, setVmmlData] = useState(null);
const [editableArray, setEditableArray] = useState([]);
const [isMounted, setIsMounted] = useState(false);
const timelineRef = useRef(null);
// 确保组件只在客户端渲染
useEffect(() => {
setIsMounted(true);
}, []);
useEffect(() => {
if (!vmml || !isMounted) return;
// 处理vmml数据
const processedData = JSON.parse(JSON.stringify(vmml));
// ... 你的数据处理逻辑
setVmmlData(processedData);
}, [vmml, isMounted]);
useEffect(() => {
if (vmmlData && isMounted) {
// 提取可编辑的元素ID
const arr = [];
const animateVmml = vmmlData.template.tracks.filter(item => item.type !== 0);
animateVmml.forEach(trackItem => {
trackItem.clips?.forEach(clipItem => {
arr.push(clipItem.id);
});
});
setEditableArray(arr);
}
}, [vmmlData, isMounted]);
// 只在客户端且数据准备好后才渲染
if (!isMounted || !vmmlData) {
return <div>Loading...</div>;
}
return (
<div className="player_wrapper">
<VmmlTimeline
ref={timelineRef}
vmml={vmmlData}
editableArray={editableArray}
pauseWhenBuffering
rsaData={{}}
switchConfig={{ AI_COMPONENT: false, TEXT_RSA: false }}
onTimelineReady={() => {
console.log('Timeline is ready');
}}
/>
</div>
);
};
export default Player;如果上面的方法还不行,使用这个更安全的版本:
import { useRef, useState, useEffect } from "react";
import dynamic from "next/dynamic";
const VmmlTimeline = dynamic(
() => import('@versa_ai/vmml-timeline').then((mod) => mod.VmmlTimeline),
{
ssr: false,
loading: () => <div>Loading timeline...</div>
}
);
const Player = ({ vmml }) => {
const [vmmlData, setVmmlData] = useState(null);
const [editableArray, setEditableArray] = useState([]);
const [canRender, setCanRender] = useState(false);
const timelineRef = useRef(null);
useEffect(() => {
// 确保window对象存在且DOM已加载
if (typeof window !== 'undefined') {
const timer = setTimeout(() => {
setCanRender(true);
}, 100); // 添加小延迟确保DOM完全准备好
return () => clearTimeout(timer);
}
}, []);
useEffect(() => {
if (!vmml || !canRender) return;
const processedData = JSON.parse(JSON.stringify(vmml));
// ... 你的数据处理逻辑
setVmmlData(processedData);
}, [vmml, canRender]);
useEffect(() => {
if (vmmlData && canRender) {
const arr = [];
const animateVmml = vmmlData.template.tracks.filter(item => item.type !== 0);
animateVmml.forEach(trackItem => {
trackItem.clips?.forEach(clipItem => {
arr.push(clipItem.id);
});
});
setEditableArray(arr);
}
}, [vmmlData, canRender]);
if (!canRender || !vmmlData) {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
Loading timeline...
</div>
);
}
return (
<div className="player_wrapper">
<VmmlTimeline
ref={timelineRef}
vmml={vmmlData}
editableArray={editableArray}
pauseWhenBuffering
rsaData={{}}
switchConfig={{ AI_COMPONENT: false, TEXT_RSA: false }}
onTimelineReady={() => {
console.log('Timeline is ready');
}}
/>
</div>
);
};
export default Player;