131 行
3.9 KiB
JavaScript
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");
|