优化速度
This commit is contained in:
parent
9c755e7648
commit
2e8b6a43d8
@ -1,149 +1,234 @@
|
||||
<script>
|
||||
import {defineComponent} from 'vue'
|
||||
import { useBootstrapBreakpoint } from './useBreakpoint.js';
|
||||
const { breakpoint } = useBootstrapBreakpoint();
|
||||
import { defineComponent } from 'vue';
|
||||
// import { useBootstrapBreakpoint } from './useBreakpoint.js'; // Kept as in original
|
||||
// const { breakpoint } = useBootstrapBreakpoint(); // Kept as in original
|
||||
|
||||
export default defineComponent({
|
||||
name: "HeaderVideo",
|
||||
props: {
|
||||
// 给图片设置替代文本
|
||||
alt: { type: String, default: '' },
|
||||
// 帧率(帧/秒),默认 20
|
||||
fps: { type: Number, default: 15 },
|
||||
// 自动播放,默认 true
|
||||
autoplay: { type: Boolean, default: true },
|
||||
// 播放完毕后不循环,触发跳转逻辑
|
||||
loop: { type: Boolean, default: false },
|
||||
// 帧总数,默认 115
|
||||
framesCount: { type: Number, default: 82 },
|
||||
// 帧图所在目录
|
||||
basePath: { type: String, default: '/openvideo/' }
|
||||
framesCount: { type: Number, default: 82 }, // Max frame index, so 83 frames total (0-82)
|
||||
basePath: { type: String, default: '/openvideo/' },
|
||||
// New prop: Percentage of frames to load before starting playback (0.0 to 1.0)
|
||||
playbackStartThreshold: { type: Number, default: 0.3 }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentFrame: 0,
|
||||
timer: null,
|
||||
isLoading: true, // 加载状态
|
||||
loadingProgress: 0, // 加载进度
|
||||
preloadedImages: [] // 预加载的图片缓存
|
||||
isLoading: true, // True while loading initial frames needed for playback
|
||||
loadingProgress: 0, // Progress for loading initial frames (0-100%)
|
||||
preloadedImages: [], // Array to store preloaded Image objects
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
frames() {
|
||||
const arr = [];
|
||||
for (let i = 0; i <= this.framesCount; i++) {
|
||||
const name = String(i).padStart(2, '0') + '.jpg';
|
||||
arr.push(this.basePath + name);
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
// Number of frames that need to be loaded before playback can start
|
||||
framesNeededForPlayback() {
|
||||
if (this.frames.length === 0) return 0;
|
||||
// Ensure at least 1 frame is loaded if there are frames and threshold > 0
|
||||
const calculatedFrames = Math.ceil(this.frames.length * this.playbackStartThreshold);
|
||||
return Math.max(1, calculatedFrames);
|
||||
},
|
||||
// Source for the current frame image, using preloaded images
|
||||
currentFrameSrc() {
|
||||
if (this.preloadedImages[this.currentFrame] && this.preloadedImages[this.currentFrame].src) {
|
||||
return this.preloadedImages[this.currentFrame].src;
|
||||
}
|
||||
// Fallback if the image isn't loaded (e.g., returns empty string, browser shows alt or broken icon)
|
||||
return '';
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
handleVideoEnded:function (event){
|
||||
methods: {
|
||||
handleVideoEnded() { // For the <video> element, not the frame animation
|
||||
window.location.href = "/Homepage";
|
||||
},
|
||||
preloadAllImages() {
|
||||
|
||||
initializeAndPreload() {
|
||||
if (this.frames.length === 0) {
|
||||
console.warn("No frames to display.");
|
||||
this.isLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
this.loadingProgress = 0;
|
||||
this.preloadedImages = new Array(this.frames.length).fill(null);
|
||||
|
||||
let loadedCount = 0;
|
||||
const totalFrames = this.frames.length;
|
||||
const framesToLoadInitially = this.framesNeededForPlayback;
|
||||
|
||||
// 创建Promise数组
|
||||
const preloadPromises = this.frames.map((src, index) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (framesToLoadInitially === 0) {
|
||||
console.warn("framesNeededForPlayback is 0. Starting playback immediately and loading all frames in background.");
|
||||
this.isLoading = false;
|
||||
if (this.autoplay) {
|
||||
this.start();
|
||||
}
|
||||
this.preloadRemainingFrames(0); // Load all frames
|
||||
return;
|
||||
}
|
||||
|
||||
let initialLoadedCount = 0;
|
||||
const initialLoadPromises = [];
|
||||
|
||||
for (let i = 0; i < framesToLoadInitially; i++) {
|
||||
// Ensure we don't try to load beyond available frames if threshold is 1.0 and framesNeededForPlayback equals frames.length
|
||||
if (i >= this.frames.length) break;
|
||||
|
||||
const src = this.frames[i];
|
||||
initialLoadPromises.push(
|
||||
new Promise((resolve) => { // No reject, always resolve to allow progress
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
this.preloadedImages[index] = img;
|
||||
loadedCount++;
|
||||
this.loadingProgress = Math.floor(loadedCount / totalFrames * 100);
|
||||
this.preloadedImages[i] = img;
|
||||
initialLoadedCount++;
|
||||
this.loadingProgress = Math.floor(initialLoadedCount / framesToLoadInitially * 100);
|
||||
resolve(img);
|
||||
};
|
||||
|
||||
img.onerror = (e) => {
|
||||
console.error(`无法加载图片: ${src}`, e);
|
||||
loadedCount++;
|
||||
this.loadingProgress = Math.floor(loadedCount / totalFrames * 100);
|
||||
reject(e);
|
||||
console.error(`Error loading initial frame ${src}:`, e);
|
||||
initialLoadedCount++; // Still count as processed for progress bar
|
||||
this.loadingProgress = Math.floor(initialLoadedCount / framesToLoadInitially * 100);
|
||||
resolve(null); // Resolve with null, preloadedImages[i] will remain null
|
||||
};
|
||||
|
||||
img.src = src;
|
||||
});
|
||||
});
|
||||
|
||||
// 所有帧加载完成后开始播放
|
||||
Promise.all(preloadPromises)
|
||||
.then(() => {
|
||||
console.log("所有帧预加载完成");
|
||||
this.isLoading = false;
|
||||
this.start();
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("部分帧加载失败,但仍继续播放");
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(initialLoadPromises)
|
||||
.then(() => {
|
||||
console.log("Initial frames loaded, starting playback.");
|
||||
this.isLoading = false;
|
||||
if (this.autoplay) {
|
||||
this.start();
|
||||
});
|
||||
}
|
||||
// Start preloading the rest of the frames
|
||||
if (framesToLoadInitially < this.frames.length) {
|
||||
this.preloadRemainingFrames(framesToLoadInitially);
|
||||
} else {
|
||||
console.log("All frames were loaded in the initial batch.");
|
||||
}
|
||||
})
|
||||
// .catch is not strictly needed here because individual promises always resolve
|
||||
// If it were to catch, it would imply a fundamental issue with Promise.all itself
|
||||
;
|
||||
},
|
||||
|
||||
preloadRemainingFrames(startIndex) {
|
||||
if (startIndex >= this.frames.length) {
|
||||
// This case should be handled by the caller, but good to have a guard
|
||||
console.log("No remaining frames to preload or startIndex is out of bounds.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Preloading remaining ${this.frames.length - startIndex} frames from index ${startIndex}...`);
|
||||
for (let i = startIndex; i < this.frames.length; i++) {
|
||||
const src = this.frames[i];
|
||||
// Skip if already loaded (e.g. if an error occurred and this is retried, or some other logic)
|
||||
if (this.preloadedImages[i]) continue;
|
||||
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
this.preloadedImages[i] = img;
|
||||
};
|
||||
img.onerror = (e) => {
|
||||
console.error(`Error loading remaining frame ${src}:`, e);
|
||||
// preloadedImages[i] will remain null for this frame
|
||||
};
|
||||
img.src = src;
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
if (this.timer) return;
|
||||
if (this.timer || this.frames.length === 0) return;
|
||||
|
||||
// Ensure playback doesn't start if it's still in the initial loading phase
|
||||
// (though initializeAndPreload should manage this by setting isLoading=false first)
|
||||
if (this.isLoading) {
|
||||
console.warn("Attempted to start playback while still in initial loading phase.");
|
||||
return;
|
||||
}
|
||||
|
||||
const interval = 1000 / this.fps;
|
||||
|
||||
this.timer = setInterval(() => {
|
||||
const next = this.currentFrame + 1;
|
||||
const nextFrame = this.currentFrame + 1;
|
||||
|
||||
if (next < this.frames.length) {
|
||||
this.currentFrame = next;
|
||||
if (nextFrame < this.frames.length) {
|
||||
this.currentFrame = nextFrame;
|
||||
} else {
|
||||
// Reached the end
|
||||
if (!this.loop) {
|
||||
this.stop();
|
||||
setTimeout(() => {
|
||||
window.location.href = "/Homepage";
|
||||
}, 100);
|
||||
} else {
|
||||
this.currentFrame = 0; // Loop back
|
||||
}
|
||||
}
|
||||
}, interval);
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (this.timer) {
|
||||
clearInterval(this.timer)
|
||||
this.timer = null
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
frames() {
|
||||
const arr = []
|
||||
for (let i = 0; i <= this.framesCount; i++) {
|
||||
// 保证五位数文件名,高位补零
|
||||
const name = String(i).padStart(2, '0') + '.jpg'
|
||||
arr.push(this.basePath + name)
|
||||
}
|
||||
return arr
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if(this.$isMobile.phone)
|
||||
{
|
||||
this.preloadAllImages();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.$isMobile.phone) { // Assuming $isMobile is correctly set up
|
||||
this.initializeAndPreload();
|
||||
} else {
|
||||
// Desktop/tablet uses <video> tag, existing logic
|
||||
const videos = document.querySelectorAll('.startvideo');
|
||||
if (videos.length > 0) {
|
||||
videos.forEach(video => {
|
||||
video.addEventListener('ended', this.handleVideoEnded);
|
||||
console.log('为视频添加了 ended 事件监听器:', video);
|
||||
});
|
||||
} else {
|
||||
console.warn("没有找到视频元素。");
|
||||
console.warn("No video elements found for desktop/tablet.");
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stop()
|
||||
this.stop();
|
||||
},
|
||||
watch: {
|
||||
autoplay(val) {
|
||||
val ? this.start() : this.stop()
|
||||
autoplay(val, oldVal) {
|
||||
if (val === oldVal) return; // No change
|
||||
|
||||
if (this.$isMobile.phone) { // Only apply to frame animation
|
||||
if (val && !this.timer && !this.isLoading) {
|
||||
// If autoplay turned on, not already playing, and not in initial load
|
||||
this.start();
|
||||
} else if (!val && this.timer) {
|
||||
// If autoplay turned off and currently playing
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// Optional: If framesCount or basePath could change dynamically and require reloading
|
||||
// framesCount() { this.initializeAndPreload(); },
|
||||
// basePath() { this.initializeAndPreload(); },
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="beginroot" class="pageroot">
|
||||
<div id="videodiv">
|
||||
<!-- 加载进度条 -->
|
||||
<!-- Loading progress for initial frames on mobile -->
|
||||
<div v-if="$isMobile.phone && isLoading" class="loading-container">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" :style="{width: loadingProgress + '%'}"></div>
|
||||
@ -151,13 +236,15 @@ export default defineComponent({
|
||||
<div class="loading-text">加载中 {{ loadingProgress }}%</div>
|
||||
</div>
|
||||
|
||||
<!-- Frame animation player for mobile, shown after initial load -->
|
||||
<img
|
||||
:src="frames[currentFrame]"
|
||||
v-if="$isMobile.phone && !isLoading"
|
||||
:src="currentFrameSrc"
|
||||
id="beginwep"
|
||||
:alt="alt"
|
||||
class="simple-frame-player"
|
||||
v-if="$isMobile.phone && !isLoading"
|
||||
/>
|
||||
<!-- Video elements for tablet/desktop (unchanged) -->
|
||||
<video v-else-if="$isMobile.tablet" poster="/logobeginPCpld.png"
|
||||
id="tbstart" muted autoplay controls="controls"
|
||||
playsinline webkit-playsinline
|
||||
@ -187,7 +274,7 @@ export default defineComponent({
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "src/publicstyle.scss";
|
||||
@import "src/publicstyle.scss"; // Assuming this path is correct relative to your project structure
|
||||
.loading-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
@ -208,7 +295,7 @@ export default defineComponent({
|
||||
.progress-bar {
|
||||
height: 100%;
|
||||
background-color: white;
|
||||
transition: width 0.3s ease;
|
||||
transition: width 0.3s ease; /* Smooth progress bar transition */
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
@ -220,7 +307,8 @@ export default defineComponent({
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#wepstart{
|
||||
#wepstart{ // This style block seems to target an element not in the provided template.
|
||||
// If #beginwep is sometimes referred to as #wepstart, it's covered by #beginwep styles.
|
||||
@include media-breakpoint-between(xs, md) {
|
||||
visibility: visible;
|
||||
height: 100%!important;
|
||||
@ -232,25 +320,25 @@ export default defineComponent({
|
||||
}
|
||||
#pcstart{
|
||||
@include media-breakpoint-between(xs, md) {
|
||||
visibility: collapse;
|
||||
visibility: collapse; // Hidden on smaller screens where frame animation or tablet video is used
|
||||
}
|
||||
@include media-breakpoint-up(md) {
|
||||
visibility: visible;
|
||||
visibility: visible; // Visible on larger screens
|
||||
}
|
||||
}
|
||||
#beginbottom {
|
||||
flex-shrink: 0; /* 添加这一行,防止此元素被压缩 */
|
||||
flex-shrink: 0;
|
||||
a {
|
||||
text-decoration: none;
|
||||
width: 100%; /* 确保链接占满整个宽度 */
|
||||
width: 100%;
|
||||
|
||||
.normalcontenttitle {
|
||||
transition: color 0.3s ease;
|
||||
color: white;
|
||||
text-align: center; /* 文本居中 */
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center; /* flex布局下的水平居中 */
|
||||
align-items: center; /* 垂直居中对齐图标 */
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#intoa{
|
||||
@include media-breakpoint-between(xs, md) {
|
||||
@ -271,34 +359,25 @@ export default defineComponent({
|
||||
}
|
||||
#videodiv{
|
||||
width: 100%;
|
||||
//height: 100%;
|
||||
flex:1;
|
||||
min-height: 0;
|
||||
background-color: black;
|
||||
overflow: hidden;
|
||||
@include media-breakpoint-between(xs, md) {
|
||||
}
|
||||
@include media-breakpoint-up(md) {
|
||||
}
|
||||
position: relative; /* Added for absolute positioning of loading-container */
|
||||
}
|
||||
.startvideo{
|
||||
.startvideo{ // Styles for <video> elements
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
#beginwep{
|
||||
#beginwep{ // Styles for the <img> frame player
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#intopagesvg{
|
||||
height: 100%;
|
||||
width: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
#intopagesvg {
|
||||
height: 1em;
|
||||
height: 1em; // Adjusted for better alignment with text
|
||||
width: auto;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
@ -309,8 +388,8 @@ export default defineComponent({
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center; /* 添加这一行使内容水平居中 */
|
||||
width: 100%; /* 确保宽度占满 */
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-left: 0!important;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user