Skip to content

Commit

Permalink
Add option to add a background to the song image
Browse files Browse the repository at this point in the history
  • Loading branch information
palinkiewicz committed Aug 4, 2024
1 parent 93a0ecb commit d604c91
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 7 deletions.
125 changes: 118 additions & 7 deletions classes/DOMHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ const NO_LYRICS_SELECTED =
const SPOTIFY_LOGO =
"https://upload.wikimedia.org/wikipedia/commons/2/26/Spotify_logo_with_text.svg";

const BACKGROUND_SHADOW_COLOR = "rgba(0, 0, 0, 0.4)";
const BACKGROUND_SHADOW_BORDER_RADIUS = 24;
const BACKGROUND_SHADOW_BLUR = 12;
const BACKGROUND_SHADOW_OFFSET_X = 0;
const BACKGROUND_SHADOW_OFFSET_Y = 4;
const BACKGROUND_TO_SHADOW_FACTOR = 4;

const COLORS = [
"#008fd1",
"#549aab",
Expand Down Expand Up @@ -76,6 +83,8 @@ class DOMHandler {
/** @type {Element} */
this.spotifyTagSwitch = document.querySelector("#spotify-tag");
/** @type {Element} */
this.additionalBgSwitch = document.querySelector("#additional-bg");
/** @type {Element} */
this.songImage = document.querySelector(".song-image");

this.populateColorSelection();
Expand Down Expand Up @@ -128,6 +137,12 @@ class DOMHandler {
this.spotifyTagSwitch.parentElement.classList.toggle("spotify-tag");
});

this.additionalBgSwitch.addEventListener("click", () => {
this.additionalBgSwitch.parentElement.classList.toggle(
"additional-bg"
);
});

this.downloadButton.addEventListener("click", () => {
this.downloadSongImage();
});
Expand Down Expand Up @@ -379,19 +394,115 @@ class DOMHandler {
* Downloads song image by generating canvas from its DOM elements
*/
async downloadSongImage() {
const canvas = await html2canvas(
document.querySelector(".song-image"),
{
backgroundColor: null,
scale: window.devicePixelRatio * DOWNLOAD_SCALING_FACTOR,
}
);
let canvas = await html2canvas(this.songImage, {
backgroundColor: null,
scale: window.devicePixelRatio * DOWNLOAD_SCALING_FACTOR,
});

if (
this.additionalBgSwitch.parentElement.classList.contains(
"additional-bg"
)
) {
canvas = this.addBgToDownloadCanvas(canvas);
}

canvas.toBlob((blob) => {
window.saveAs(blob, "download.png");
});
}

/**
* Adds background with shadow to the canvas
* @param {HTMLCanvasElement} canvas
* @returns {HTMLCanvasElement} canvas with background and shadow
*/
addBgToDownloadCanvas(canvas) {
const backgroundColor = this.songImage.style.backgroundColor;

const borderRadius =
BACKGROUND_SHADOW_BORDER_RADIUS *
window.devicePixelRatio *
DOWNLOAD_SCALING_FACTOR;

const shadowBlur =
BACKGROUND_SHADOW_BLUR *
window.devicePixelRatio *
DOWNLOAD_SCALING_FACTOR;

const margin = shadowBlur * BACKGROUND_TO_SHADOW_FACTOR;

const shadowOffsetX =
BACKGROUND_SHADOW_OFFSET_X *
window.devicePixelRatio *
DOWNLOAD_SCALING_FACTOR;

const shadowOffsetY =
BACKGROUND_SHADOW_OFFSET_Y *
window.devicePixelRatio *
DOWNLOAD_SCALING_FACTOR;

const shadowCanvas = document.createElement("canvas");
shadowCanvas.width = canvas.width + margin * 2;
shadowCanvas.height = canvas.height + margin * 2;
const shadowContext = shadowCanvas.getContext("2d");

shadowContext.fillStyle = backgroundColor;
shadowContext.fillRect(0, 0, shadowCanvas.width, shadowCanvas.height);

shadowContext.fillStyle = BACKGROUND_SHADOW_COLOR;
shadowContext.filter = `blur(${shadowBlur}px)`;
shadowContext.beginPath();
shadowContext.moveTo(
margin + shadowOffsetX + borderRadius,
margin + shadowOffsetY
);
shadowContext.lineTo(
margin + shadowOffsetX + canvas.width - borderRadius,
margin + shadowOffsetY
);
shadowContext.quadraticCurveTo(
margin + shadowOffsetX + canvas.width,
margin + shadowOffsetY,
margin + shadowOffsetX + canvas.width,
margin + shadowOffsetY + borderRadius
);
shadowContext.lineTo(
margin + shadowOffsetX + canvas.width,
margin + shadowOffsetY + canvas.height - borderRadius
);
shadowContext.quadraticCurveTo(
margin + shadowOffsetX + canvas.width,
margin + shadowOffsetY + canvas.height,
margin + shadowOffsetX + canvas.width - borderRadius,
margin + shadowOffsetY + canvas.height
);
shadowContext.lineTo(
margin + shadowOffsetX + borderRadius,
margin + shadowOffsetY + canvas.height
);
shadowContext.quadraticCurveTo(
margin + shadowOffsetX,
margin + shadowOffsetY + canvas.height,
margin + shadowOffsetX,
margin + shadowOffsetY + canvas.height - borderRadius
);
shadowContext.lineTo(margin + shadowOffsetX, margin + shadowOffsetY + borderRadius);
shadowContext.quadraticCurveTo(
margin + shadowOffsetX,
margin + shadowOffsetY,
margin + shadowOffsetX + borderRadius,
margin + shadowOffsetY
);
shadowContext.closePath();
shadowContext.fill();

shadowContext.filter = "none";
shadowContext.drawImage(canvas, margin, margin);

return shadowCanvas;
}

/**
* Converts cover image to base64 and sets it as image's source in order to avoid canvas CORS policy
* @param {string} url
Expand Down
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ <h2>Song image</h2>
<div class="switch"></div>
<div>Spotify tag</div>
</div>
<div class="switch-container" id="additional-bg">
<div class="switch"></div>
<div>Background</div>
</div>
</div>
</section>
</main>
Expand Down
1 change: 1 addition & 0 deletions styles/song-image.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
.song-image > .header .name {
font-size: 0.95rem;
font-weight: 700;
line-height: 1.2rem;
}

.song-image > .header .authors {
Expand Down
4 changes: 4 additions & 0 deletions styles/wizard.css
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,10 @@
left: calc(100% - 1.75rem);
}

.additional-bg #additional-bg .switch::after {
left: calc(100% - 1.75rem);
}

@media screen and (max-width: 450px) {
.select-song {
width: 45%;
Expand Down

0 comments on commit d604c91

Please sign in to comment.