From 25818ae89f757339bedde9b45b7cd2a2c7edfed7 Mon Sep 17 00:00:00 2001 From: Glen Lowland <glenlowland@proton.me> Date: Mon, 1 May 2023 23:06:19 +0100 Subject: [PATCH] Fix seekbar preview aspect ratio and cropping This PR fixes two issue observed with the seekbar preview: - sometimes images got cropped incorrectly at the end of the video due to image having less frames that server tells us - so calculate it in place; - set canvas width and height based on actual frame data so it follows the video aspect ratio rather than hard-coded value. --- src/components/VideoPlayer.vue | 43 ++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/components/VideoPlayer.vue b/src/components/VideoPlayer.vue index 40378759..8693adb8 100644 --- a/src/components/VideoPlayer.vue +++ b/src/components/VideoPlayer.vue @@ -6,7 +6,7 @@ :class="{ 'player-container': !isEmbed }" > <video ref="videoEl" class="w-full" data-shaka-player :autoplay="shouldAutoPlay" :loop="selectedAutoLoop" /> - <canvas height="130" width="230" id="preview" /> + <canvas id="preview" /> <button v-if="inSegment" class="skip-segment-button" @@ -697,24 +697,37 @@ export default { }); }, async showSeekbarPreview(position) { - let frame = this.getFrame(position); - let originalImage = await this.loadImage(frame.url); + const frame = this.getFrame(position); + const originalImage = await this.loadImage(frame.url); if (!this.isHoveringTimebar) return; - let seekBar = document.querySelector(".shaka-seek-bar"); - let canvas = document.querySelector("#preview"); - let ctx = canvas.getContext("2d"); + const seekBar = document.querySelector(".shaka-seek-bar"); + const canvas = document.querySelector("#preview"); + const ctx = canvas.getContext("2d"); // get the new sizes for the image to be drawn into the canvas const originalWidth = originalImage.naturalWidth; const originalHeight = originalImage.naturalHeight; - const offsetX = originalWidth * (frame.positionX / frame.framesPerPageX); - const offsetY = originalHeight * (frame.positionY / frame.framesPerPageY); - const newWidth = originalWidth / frame.framesPerPageX; - const newHeight = originalHeight / frame.framesPerPageY; + // image can have less frames than server told us so calculate them ourselves + const imageFramesPerPageX = originalImage.naturalWidth / frame.frameWidth; + const imageFramesPerPageY = originalImage.naturalHeight / frame.frameHeight; + const offsetX = originalWidth * (frame.positionX / imageFramesPerPageX); + const offsetY = originalHeight * (frame.positionY / imageFramesPerPageY); + canvas.width = frame.frameWidth > 100 ? frame.frameWidth : frame.frameWidth * 2; + canvas.height = frame.frameWidth > 100 ? frame.frameHeight : frame.frameHeight * 2; // draw the thumbnail preview into the canvas by cropping only the relevant part - ctx.drawImage(originalImage, offsetX, offsetY, newWidth, newHeight, 0, 0, canvas.width, canvas.height); + ctx.drawImage( + originalImage, + offsetX, + offsetY, + frame.frameWidth, + frame.frameHeight, + 0, + 0, + canvas.width, + canvas.height, + ); // calculate the thumbnail preview offset and display it const seekbarPadding = 2; // percentage of seekbar padding @@ -727,7 +740,7 @@ export default { // ineffective algorithm to find the thumbnail corresponding to the currently hovered position in the video getFrame(position) { let startPosition = 0; - let framePage = this.video.previewFrames.at(-1); + const framePage = this.video.previewFrames.at(-1); for (let i = 0; i < framePage.urls.length; i++) { for (let positionY = 0; positionY < framePage.framesPerPageY; positionY++) { for (let positionX = 0; positionX < framePage.framesPerPageX; positionX++) { @@ -737,8 +750,8 @@ export default { url: framePage.urls[i], positionX: positionX, positionY: positionY, - framesPerPageX: framePage.framesPerPageX, - framesPerPageY: framePage.framesPerPageY, + frameWidth: framePage.frameWidth, + frameHeight: framePage.frameHeight, }; } startPosition = endPosition; @@ -750,7 +763,7 @@ export default { // creates a new image from an URL loadImage(url) { return new Promise(r => { - let i = new Image(); + const i = new Image(); i.onload = () => r(i); i.src = url; });