辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

Write a small tool to conveniently and quickly add a banner to an article.

image.png

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.

Weird sizes + repetitive editing + disposable images + consistent style = a tool to generate template images.

Currently, I don't need complex drag-and-drop functionality or diverse materials. It seems that a simple image background + text + adjustable size will meet the requirements.

So I decided to make one. I'll start with a simple version and also accumulate some commonly used code.

The current implementation looks like this, visit the address: https://ijust.cc/tools/canvas

image.png

Approach#

  • Use a fixed template with editable text
  • Use html2canvas (specifically, another solution) to generate images
  • Allow for variable backgrounds to avoid inconsistent styles

Technology Selection#

shadcn Styles#

The functionality is not complex. I'll create a form and apply some styles to generate the image. I'm confident in completing the task, so I'll try learning a tailwind-related component library.

The blog's website already uses tailwindcss + varlet components overall, so I don't want to introduce components from element-plus/arco-design. The components provided by tailwindcss seem to have minimal intrusion and should not significantly affect the bundle size.

While browsing my notes, I came across shadcn, which I had previously bookmarked. Currently, shadcn-vue is progressing rapidly and has a relatively simple style, with radix as the underlying low-level encapsulation. It should be good. Once the shadcn template library is established, it should be even better.

So I choose shadcn-vue as the component library and get started.

It feels a bit awkward at first, but after looking at it for a while, it becomes more familiar. The style is very simple, and I choose the gray style, which is not too dull.

image.png

html-to-image for Image Generation#

To generate images based on HTML, the famous html2canvas is quite 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, and clear API, so I'll use it.

Dynamic Color with HSL#

For the background color, I prefer slightly muted colors. The regular taiwindcss colors don't seem to be enough, so I thought of using colors with the same saturation and brightness but different hues.

This is something I often do in Photoshop, quickly changing the overall color by dragging the hue.

I often use the HSB color model for this, where only the hue needs to be modified. Here's an example:

image.png

This is achieved using the hue-rotate filter in CSS:

.hue {
  filter: hue-rotate(45deg);
}

However, it didn't work as expected. Some elements, such as character images, should not be affected by the filter, so it was difficult to achieve the desired effect. It was also cumbersome to modify each background and text individually.

Instead, I found that CSS has HSL (Hue, Saturation, Lightness) but not HSB. So I asked for advice:

Explain 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 do you 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 hue value to a decimal between 0 and 1
  h = h / 360;

  // Convert 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 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 RGB values to integers and limit them to the range 0 to 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. There are ready-made methods available, and they worked well. +1 for the utility library, and I learned something new.

After some experimentation, I was able to dynamically modify the hue in CSS:

<div :style='{
background: 'hsl(' + config.hue[0] + ',48.1%,48.6%)',
}'>Dynamic Background<div>

In Chrome, the actual result is still displayed as RGB, but it feels comfortable to use. The color changes when dragging the slider.

I also added will-change and transition for a smoother color change.

Addendum: Later, I added a 45-degree tilted linear gradient from color A to color B. Here, the hue color is used as A, and the corresponding color is obtained by adding a bit, +50, to the hue. The actual effect is quite good.

Addendum: Later, I added a low-dimensional background image, which also looks great. I used:

Results#

image.png

image.png

image.png

Outlook#

In the future, I can try making it a standalone service with customizable images.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.