feat: export video done
diff --git a/src/components/BBody.vue b/src/components/BBody.vue
index 1ad168f..d0d1638 100644
--- a/src/components/BBody.vue
+++ b/src/components/BBody.vue
@@ -1,80 +1,126 @@
<template>
- <div class="w-full h-full"
- v-loading="isExportingVideo"
- element-loading-text="视频生成中"
- >
+ <div class="w-full h-full">
<div class="grid grid-cols-12 h-full text-sm">
- <el-card class="box-card col-span-3">
+ <el-card class="box-card col-span-3 scroll-card">
<h1 slot="header" class="clearfix text-xl">
{{$t('toolName')}}
</h1>
<div id="el-config" class="align-middle">
- <el-form ref="form">
- <div class="grid grid-cols-3 form-row">
- <label class="col-span-1">{{$t('chartTitle')}}</label>
- <el-input
- id="input-title"
- size="medium"
- class="col-span-2"
- v-model="title"
- @change="runChart"
- >
- </el-input>
- </div>
- <div class="grid grid-cols-3 form-row">
- <label class="col-span-1">显示排名上限</label>
- <el-input
- id="input-max"
- type="number"
- value=""
- size="medium"
- class="col-span-2"
- v-model="maxDataCnt"
- @change="runChart"
- >
- </el-input>
- </div>
- <div class="grid grid-cols-3 form-row">
- <label class="col-span-1">每行动画时长(毫秒)</label>
- <el-input
- id="input-animation-duration"
- type="number"
- value="5000"
- size="medium"
- class="col-span-2"
- v-model="animationDuration"
- @change="runChart"
- >
- </el-input>
- </div>
- <div class="grid grid-cols-3 form-row">
- <label class="col-span-1">视频宽度</label>
- <el-input
- type="number"
- size="medium"
- class="col-span-2"
- v-model="width"
- >
- </el-input>
- </div>
- <div class="grid grid-cols-3 form-row">
- <label class="col-span-1">视频高度</label>
- <el-input
- type="number"
- size="medium"
- class="col-span-2"
- v-model="height"
- >
- </el-input>
- </div>
- <el-form-item>
- <el-button @click="download" type="primary">下载代码</el-button>
- <el-button @click="downloadVideo">生成视频</el-button>
- </el-form-item>
+ <el-form ref="form" :disabled="isExportingVideo">
+ <h2>图表设置</h2>
+ <el-row>
+ <el-col :span="12">
+ {{$t('chartTitle')}}
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ id="input-title"
+ size="medium"
+ class="col-span-1"
+ v-model="title"
+ @change="runChart"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ 显示排名上限
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ id="input-max"
+ type="number"
+ value=""
+ size="medium"
+ class="col-span-2"
+ placeholder="为空则显示所有"
+ v-model="maxDataCnt"
+ @change="runChart"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ 每行动画时长<span class="hint">(毫秒)</span>
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ id="input-animation-duration"
+ type="number"
+ value="5000"
+ size="medium"
+ class="col-span-2"
+ v-model="animationDuration"
+ @change="runChart"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-button @click="download" type="primary">
+ <i class="el-icon-download"></i>
+ 下载代码
+ </el-button>
+ </el-row>
+
+ <el-divider></el-divider>
+
+ <h2>视频设置</h2>
+ <el-row>
+ <el-col :span="12">
+ 视频宽度
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ type="number"
+ size="medium"
+ class="col-span-2"
+ v-model="width"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ 视频高度
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ type="number"
+ size="medium"
+ class="col-span-2"
+ v-model="height"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-col :span="12">
+ 视频帧率<span class="hint">(FPS)</span>
+ </el-col>
+ <el-col :span="12">
+ <el-input
+ type="number"
+ size="medium"
+ class="col-span-2"
+ v-model="fps"
+ >
+ </el-input>
+ </el-col>
+ </el-row>
+ <el-row>
+ <el-button @click="downloadVideo">
+ <i class="el-icon-video-camera"></i>
+ 生成视频
+ </el-button>
+ </el-row>
</el-form>
</div>
</el-card>
<el-card
+ v-if="!isExportingVideo"
class="box-card col-span-4 relative"
body-style="height: 100%"
>
@@ -84,7 +130,8 @@
/>
</el-card>
<el-card
- class="box-card col-span-5 relative"
+ class="box-card relative"
+ :class="isExportingVideo ? 'col-span-9' : 'col-span-5'"
body-style="height: 100%"
>
<BChart
@@ -116,6 +163,7 @@
animationDuration: 3000,
width: 1280,
height: 720,
+ fps: 30,
videoPercentage: 40,
isExportingVideo: false
}
@@ -158,16 +206,34 @@
},
async downloadVideo() {
- // this.isExportingVideo = true;
- const isSuccess = await (this.$refs.bchart as any).captureVideo(this.width, this.height);
- // this.isExportingVideo = false;
- if (!isSuccess) {
- // this.$notify.error({
- // title: '导出失败!',
- // message: '建议使用最新版 Chrome 或 Firefox',
- // duration: 0,
- // position: 'bottom-left'
- // });
+ if (!this.width || !this.height) {
+ this.$notify.error({
+ title: '视频高度宽度错误!',
+ message: '高度宽度不能为 0',
+ duration: 0,
+ position: 'bottom-left'
+ });
+ return;
+ }
+
+ this.isExportingVideo = true;
+ const isSuccess = await (this.$refs.bchart as any).captureVideo(this.width, this.height, this.fps);
+ this.isExportingVideo = false;
+ if (isSuccess) {
+ this.$notify({
+ title: '导出成功!',
+ type: 'success',
+ duration: 3000,
+ position: 'bottom-left'
+ });
+ }
+ else {
+ this.$notify.error({
+ title: '导出失败!',
+ message: '建议使用最新版 Chrome 或 Firefox',
+ duration: 0,
+ position: 'bottom-left'
+ });
}
}
}
@@ -175,8 +241,37 @@
</script>
<style scoped>
+.scroll-card {
+ overflow-y: auto;
+}
+
+.el-col-12 {
+ place-self: center;
+}
+
+h1 {
+ margin-bottom: 15px;
+ font-weight: bold;
+}
+
+h2 {
+ margin-bottom: 15px;
+ font-size: 16px;
+ font-weight: bold;
+}
+
+.hint {
+ color: #999;
+ font-size: 12px;
+}
+
+.el-button i {
+ display: inline-block;
+ margin-right: 2px;
+}
+
@layer utilities {
- .form-row {
+ .el-row {
@apply my-3;
label {
diff --git a/src/components/BChart.vue b/src/components/BChart.vue
index c0f83a2..c0ab889 100644
--- a/src/components/BChart.vue
+++ b/src/components/BChart.vue
@@ -1,21 +1,21 @@
<template>
<div>
- <div slot="header" class="clearfix text-base">
+ <div slot="header" class="clearfix text-base" v-if="!isExportingVideo">
{{$t('preview')}}
<a href="javascript:;" @click="run()">
<i class="el-icon-refresh"></i>
</a>
</div>
+ <div slot="header" class="clearfix text-base" v-if="isExportingVideo">
+ <i class="el-icon-loading"></i>
+ {{$t('exporting')}}
+ </div>
<div
id="bar-race-preview"
ref="chart"
class="absolute bottom-4 top-14 left-5 right-5 border"
- :class="isHidden ? 'hidden' : ''"
>
</div>
- <div id="chart-hint" v-if="isHidden">
- 视频生成中无法预览
- </div>
</div>
</template>
@@ -50,7 +50,7 @@
data() {
return {
timeoutHandlers: [],
- isHidden: false
+ isExportingVideo: false
};
},
watch: {
@@ -82,13 +82,28 @@
}
},
- captureVideo(width?: number, height?: number): Promise<boolean> {
+ captureVideo(width: number, height: number, fps: number): Promise<boolean> {
return new Promise(resolve => {
try {
- this.isHidden = true;
+ this.isExportingVideo = true;
this.doResetChart(width, height);
- const canvas = chart.getDom().children[0].children[0] as HTMLCanvasElement;
- const recorder = canvasRecord(canvas);
+ const container = chart.getDom();
+ const canvas = container.children[0].children[0] as HTMLCanvasElement;
+ if (container.clientHeight) {
+ if (container.clientWidth / container.clientHeight > width / height) {
+ canvas.style.height = container.clientHeight + 'px';
+ canvas.style.width = container.clientHeight / height * width + 'px';
+ }
+ else {
+ canvas.style.width = container.clientWidth + 'px';
+ canvas.style.height = container.clientWidth / width * height + 'px';
+ }
+ }
+
+ const recorder = canvasRecord(canvas, {
+ frameRate: fps || 30,
+ filename: this.title || this.$t('toolName')
+ });
time = Date.now();
recorder.start();
@@ -103,16 +118,16 @@
hasError = true;
}
- this.isHidden = false;
+ this.isExportingVideo = false;
setTimeout(() => {
this.run();
+ resolve(!hasError);
});
- resolve(hasError);
});
}
catch (e) {
console.error(e);
- this.isHidden = false;
+ this.isExportingVideo = false;
resolve(false);
}
});
@@ -167,7 +182,9 @@
}],
grid: {
right: 60,
- bottom: 30
+ bottom: 30,
+ left: 20,
+ containLabel: true
},
title: [{
text: (this.chartData as any)[headerLength][0],
diff --git a/src/components/BTable.vue b/src/components/BTable.vue
index 81a0c33..c3b883a 100644
--- a/src/components/BTable.vue
+++ b/src/components/BTable.vue
@@ -39,7 +39,7 @@
data() {
return {
tableData: [
- ['', 'blueberry', 'banana', 'kiwi', 'watermelon']
+ ['', 'blueberry', 'kiwi', 'banana', 'watermelon']
// @ts-ignore:
.map(name => name ? this.$i18n.t(name) : ''),
// @ts-ignore:
diff --git a/src/helper/template.ts b/src/helper/template.ts
index 6f89e3c..8f49db7 100644
--- a/src/helper/template.ts
+++ b/src/helper/template.ts
@@ -82,10 +82,12 @@
}],
grid: {
right: 60,
- bottom: 30
+ bottom: 30,
+ left: 20,
+ containLabel: true
},
title: [{
- text: 'aaa',
+ text: data[headerLength][0],
right: 20,
bottom: 15,
textStyle: {
@@ -118,6 +120,9 @@
label: {
valueAnimation: true
}
+ }],
+ title: [{
+ text: data[headerLength + i + 1][0]
}]
});
removeTimeoutHandlers(timeout);
diff --git a/src/i18n/en-US.ts b/src/i18n/en-US.ts
index 2e7deaf..fda190d 100644
--- a/src/i18n/en-US.ts
+++ b/src/i18n/en-US.ts
@@ -11,5 +11,6 @@
banana: 'Banana',
kiwi: 'Kiwi',
watermelon: 'Watermelon',
- preview: 'Preview'
+ preview: 'Preview',
+ exporting: 'The video is under exporting'
};
diff --git a/src/i18n/zh-CN.ts b/src/i18n/zh-CN.ts
index 8837bda..af328e7 100644
--- a/src/i18n/zh-CN.ts
+++ b/src/i18n/zh-CN.ts
@@ -15,7 +15,8 @@
banana: '香蕉',
kiwi: '猕猴桃',
watermelon: '西瓜',
- preview: '预览'
+ preview: '预览',
+ exporting: '视频正在导出中'
};
export default langCn;
\ No newline at end of file