076video-player/index.js

131 行
3.9 KiB
JavaScript

const player = Vue.createApp({
data() {
return {
videoOgv: 'video.ogv',
videoWebm: 'video.webm',
commentJson: 'comments.json',
comments: [],
visibleComments: [],
containerWidth: 0
};
},
methods: {
async loadComments() {
try {
const resp = await fetch(this.commentJson);
if (!resp.ok) {
throw new Error(`HTTPエラー: ${resp.status}`);
}
const data = await resp.json();
this.comments = data.map((comment, index) => {
return { ...comment, yPos: (index % 10) * 30 };
});
} catch (error) {
console.error("コメントを受取に失敗:", error);
}
},
updateComments() {
const currentTime = this.$refs.videoPlayer.currentTime;
let yPos = 0;
this.visibleComments = this.comments.filter(comment => {
const isVisible = currentTime >= comment.timestamp && currentTime <= (comment.timestamp + 5);
if (isVisible && typeof comment.yPos === 'undefined') {
comment.yPos = yPos;
yPos += 30;
}
return isVisible;
});
this.$nextTick(() => {
this.animateComments();
});
},
animateComments() {
const commentsElements = this.$refs.videoContainer.querySelectorAll('.comment');
commentsElements.forEach(el => {
const commentWidth = el.offsetWidth;
let totalDistance = this.containerWidth + commentWidth;
totalDistance -= (100-commentWidth);
el.style.transition = 'transform 5s linear';
el.style.transform = `translateX(-${totalDistance}px)`;
});
},
updateContainerWidth() {
const container = this.$refs.videoContainer;
this.containerWidth = container ? container.offsetWidth : 0;
},
calculateCommentWidth(text) {
const tempDiv = document.createElement("div");
tempDiv.style.position = "absolute";
tempDiv.style.whiteSpace = "nowrap";
tempDiv.style.fontSize = "50px";
tempDiv.textContent = text;
document.body.appendChild(tempDiv);
const width = tempDiv.clientWidth;
document.body.removeChild(tempDiv);
return width;
},
createAnimationKeyframes() {
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerHTML = `
@keyframes scrollComment {
from { transform: translateX(${this.containerWidth}px); }
to { transform: translateX(-${this.containerWidth}px); }
}
`;
document.head.appendChild(styleSheet);
},
pauseComments() {
const commentsElements = this.$refs.videoContainer.querySelectorAll('.comment');
commentsElements.forEach(el => {
const computedStyle = window.getComputedStyle(el);
const transform = computedStyle.transform || computedStyle.webkitTransform;
el.style.transform = transform;
el.style.transition = 'none';
});
},
playComments() {
this.animateComments();
},
attachVideoEventListeners() {
const videoPlayer = this.$refs.videoPlayer;
videoPlayer.addEventListener('play', this.playComments);
videoPlayer.addEventListener('pause', this.pauseComments);
}
},
mounted() {
this.loadComments();
this.createAnimationKeyframes();
window.addEventListener('resize', () => {
this.updateContainerWidth();
this.createAnimationKeyframes();
});
this.updateContainerWidth();
this.attachVideoEventListeners();
window.addEventListener('resize', this.updateContainerWidth);
},
beforeUnmount() {
window.removeEventListener('resize', this.updateContainerWidth);
},
computed: {
commentAnimationStyle() {
return `
<style>
@keyframes scrollComment {
from { transform: translateX(0%); }
to { transform: translateX(-${this.containerWidth}px); }
}
</style>
`;
}
}
});
player.mount("#root");