Background
Today I wanted to submit a few articles to different websites and found that Juejin requires an article banner image. Creating a Juejin article collection also requires an image. Providing images is not a problem, but the sizes are all different and I only have 1:1 images by default.
Strange sizes + repetitive editing work + disposable images that don't need to be saved + consistent style = create a template image generation tool.
Currently, I don't need complex drag and drop functionality or various materials. It seems that a simple image background + text + adjustable size will meet the requirements.
So I decided to create one, starting with a simple version and also accumulating some commonly used code.
The current implementation looks like this, visit the address https://ijust.cc/tools/canvas
Production Ideas
- Fix a commonly used template, with editable text
- Use html2canvas (specifically using an alternative solution) to generate images
- Avoid different styles, allow for variable backgrounds
Technology Selection
shadcn style
The functionality is not complex, just write a form, stack some styles, and generate images. I am confident in completing the task, so I will try to learn a tailwind-related component library.
The blog website already uses tailwindcss + varlet components as a whole, and I don't want to introduce element-plus/arco-design related components again. It seems that the components provided by tailwindcss have a small invasion and should not have a significant impact on the bundle size.
Looking through my notes, I came across shadcn, which I had already bookmarked. Currently, shadcn-vue is progressing very quickly, with a relatively simple style and radix as a low-level encapsulation, so it should be good. Once the shadcn template library is established, it should be even better.
So I chose shadcn-vue as the component library and started working on it.
It felt a bit awkward at first, but after looking at it for a while, it became more pleasing to the eye. The style is very simple, and I chose the gray style, which is not too dull.
html-to-image for image generation
Based on HTML, the famous html2canvas is very good. I searched on https://npmtrends.com/ and found that https://github.com/bubkoo/html-to-image is also good. It has a high update frequency, modern documentation style, clear API, so I decided to use it.
Dynamic color HSL
For the background color, I prefer slightly simpler colors. The regular taiwindcss colors don't seem to be enough, so I wanted more freedom. So I turned my attention to the idea of using different hues with the same saturation and brightness.
This is something I often use in Photoshop, where I can quickly change the overall color by dragging the hue.
This is done using the HSB color model, where only the hue needs to be modified. Here is an example:
I tried using the hue-rotate
filter, but it didn't work well. Some elements, such as character images, should not be affected by the filter, so it was difficult to achieve. It would be cumbersome to modify the background and text one by one.
So I decided to dynamically modify the CSS. But I found that CSS does not have HSB, only HSL. So I asked for advice:
Introduce the difference between HSB and HSL
HSB (Hue, Saturation, Brightness) and HSL (Hue, Saturation, Lightness) are two commonly used color models, and they have some differences in how they describe colors. Blah blah blah.
How to convert between the two?
// HSB to HSL
function hsbToHsl(h, s, b) {
let l = (2 - s) * b / 2;
s = s * b / (l < 0.5 ? l * 2 : 2 - l * 2);
return [h, s, l];
}
// HSL to HSB
function hslToHsb(h, s, l) {
let v = l + s * (l < 0.5 ? l : 1 - l);
s = 2 * s / (v + l);
return [h, s, v];
}
// Example usage
const hsbColor = [0.5, 0.7, 0.8];
const hslColor = hsbToHsl(...hsbColor);
console.log(hslColor); // [0.5, 0.4666666666666667, 0.65]
const convertedHsbColor = hslToHsb(...hslColor);
console.log(convertedHsbColor); // [0.5, 0.7, 0.8]
function hslToRgb(h: number, s: number, l: number) {
// Convert the hue value to a decimal between 0 and 1
h = h / 360;
// Convert the saturation and lightness values from percentages to decimals between 0 and 1
s = s / 100;
l = l / 100;
let r, g, b;
if (s === 0) {
// When the saturation is 0, the color is gray
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
// Convert the RGB values to integers and limit the values to between 0 and 255
r = Math.round(r * 255);
g = Math.round(g * 255);
b = Math.round(b * 255);
// return { r, g, b };
return `rgb(${r},${g},${b})`;
}
I understood and found that there are ready-made methods available. I tried them and they worked well. +1 for the utility library, and I learned something new.
After some operations, I successfully implemented dynamically modifying the hue in CSS.
<div :style='{
background: 'hsl(' + config.hue[0] + ',48.1%,48.6%)',
}'>Dynamic background<div>
In Chrome, it actually runs as RGB, but it feels comfortable to use. The color changes when dragging the slider.
I added will-change
and transition
when changing colors, which feels good.
Addendum: Later, I added a 45-degree tilted linear gradient, from A to B. Here, the hue color is A, and the corresponding color is slightly increased by +50 to get color B. The actual effect is also very good.
Addendum: Later, I added a low-dimensional background image, which also looks good. I used:
Results
Outlook
In the future, it can be tried as an independent service, with customizable images, etc.