戴兜

戴兜

Coding the world.
github
bilibili
twitter

Handcrafted a front-end weather card

The idea of ​​making a weather card component by myself has actually been there for a long time, but it is still very difficult to do (layout, data source, weather display, adaptive), and it ended up being nothing. Recently, the topic of the school club interview is to make a weather card, so I can take this opportunity to study it carefully during the National Day holiday. So today's article came into being.

~ (In fact, I basically solved the problem before the National Day holiday) ~ The current version of DouWeather is hosted in the temporary repository (https://github.com/daidr/DouWeather-temp) because the code structure was not considered and it is difficult to maintain later. You can watch this repository (https://github.com/daidr/DouWeather) to follow up on the progress. I will take the time to refactor the code and update it in this repository.

Ⅰ. Design Concept Stage

1. Source of inspiration

I positioned DouWeather (hereinafter referred to as DW) as a web widget, and based on this consideration, I referred to iOS system widgets, the new version of MIUI system widgets, Hongmeng system widgets, and Win11 widgets. I found that they all have the same characteristics: flat, rounded corners, use of sans-serif fonts, simple element style, and all four use gradients in the background or icons, making the widgets more dynamic. Among them, the Win11 widget adds a light shadow, which may be to highlight the widget from the acrylic background.

So, I followed suit and designed the sunny weather icon for DW, and used XD to design the first card style (the current medium card style of DW).

Note: The design of DouWeather is also based on the design style of Google AdSense (MD2), Mew, and other websites.

Shrink the card style of the medium class to derive a square small style widget.

I also made a diagram when I was impulsive to write a weather card. At that time, I referred to the MD style. I happened to find it from my computer when I went home during the National Day. I don't know how long ago the initial draft of the weather card was... it's quite abstract...

2. Icon design

Many elements in DW are borrowed from Xiaomi Weather, including the icons. DW tries to ensure that the overall style of the icons is simple, using large gradient backgrounds to highlight the weather characteristics. Before officially starting, I imitated Xiaomi Weather and made three icons for sunny, cloudy, and partly cloudy, which would be convenient for future design and development. The format is still svg, which controls the overall size of the component and ensures loading speed.

During the development process, inspired by my roommate, I tried to add some animation to the weather icons, but some of them were too distracting, so I gave up in the end.

3. Normal style and detail style?

Before development, I actually only planned to make two styles (small and medium). The main reason for making the normal style is that during the development process, I found that when the medium style is placed above an element with a width that is too large, it will appear empty and not beautiful enough. So based on the medium style, I increased the width and added air quality, sun protection advice, and other data displays. The detail style is purely because I love the trend forecast of Xiaomi Weather and want to replicate it in DW.

Ⅱ. Development Stage

1. How to use Web Components elegantly without relying on build tools?

Before writing the weather card, I had only used Web Components once, which was in the Genshin Impact player information query (https://ys.daidr.me). At that time, because there were many repetitive elements (character information), I wanted to try using this new thing to encapsulate them. The lesson learned is: if you don't use build tools and want to develop more elegantly, the template tag is essential, otherwise maintaining the code will be a nightmare.

2. How to display icons elegantly?

Weather icons will be heavily reused in the card (especially the detail style), and it will be difficult to maintain if there is no simple way to call them. And during development, I only designed 3 icons, so I need to consider how to easily add, delete, and modify icons in the later stage, and minimize the coupling between icon retrieval and main code. In front-end development, there are generally several ways to import icons:

① Use @font-face to import icon font files

This method is generally used for large icon fonts, such as Font Awesome and Material Icons. The advantage is that the operation is intuitive, and you can directly modify the icon display style using font-size or color. Thanks to the browser's support for colr, you can use colored icon fonts. However, the disadvantages are also obvious: it is more difficult to maintain, especially when it involves gradient filling. Currently, there is no font-making software that can elegantly complete this task. And the hook logic of some mobile phone custom fonts may cause the icon font introduced by this method to fail.

However, in DW, some icons are using this method, such as the wind direction icons. The icons are monochrome and the number is fixed and does not need to be modified frequently (8 directions), which is very suitable for this method.

② Use svg symbol

This is also a very common way to reference icons, and it has excellent compatibility. It is relatively easy to maintain and can support some animations. AI can directly export icons as symbol tags, and many build tools can also provide support for this, so there are basically no disadvantages.

However, the weather icons in DW did not use the above two methods. I encapsulated the weather icons using Web Components, which already has a similar effect to symbol, so it seems redundant to use symbol again.

Calling the encapsulated weather icons is much more convenient. You can directly use <dw-icon type="sunny"></dw-icon> to call a specific icon. Here is an example.

I plan to use gulp later, which can also provide convenience for development.

3. How to achieve adaptability?

During the development of the weather component, I realized that restoring the design draft is actually the easiest thing among them. I need to ensure that all elements in the card can be displayed in an orderly manner. I originally wanted to fix the width of each card style, which would ensure that the layout of the card is always perfect, but it would greatly reduce the versatility of the weather card, and other users of DW would not modify their layout scheme specifically for a widget. At the same time, fixed width means that the experience of the weather card on mobile devices will be very poor. But how to achieve adaptability?

The most commonly used method of adaptability is to write media queries, but I cannot use media queries because I cannot know where other developers insert the card, how to insert the card, and what is the state of the parent element of the card. I cannot determine the current state of the weather card based solely on the screen size.

I also cannot hide or display certain elements based on the card width, because in future versions, DW will modularize the data display part, allowing other developers to customize which data to display. Modifying the displayed elements without authorization may cause other developers' configurations to not be displayed as expected.

Since we are talking about judging the size of the parent container, let's talk about the implementation method. Generally speaking, the method I often use is to embed an iframe in the parent container, and listen to the size changes of the iframe to determine the size changes of the container. Perhaps in the future, we can also try CSS container queries, which can provide great convenience, but currently this feature is still in the PR stage... Compatibility list.

I once wanted to fix the width of the card. In fact, until I wrote almost all the layout and logic of DW, I still didn't find a good solution.

What inspired me was the Windows File Explorer:

The main elements of the weather card are fixed on the left and do not move, and the data display on the right shows a scroll bar based on the width of the card. The implementation is also very simple because I use flex layout. Just wrap the original data display area with a container with the style flex-grow: 1;.

4. Data display in detail style

This part is also very complicated because the club interview task mentioned chart display. At that time, I wanted to replicate a 15-day trend forecast of Xiaomi Weather to try it out. If it succeeded, I could expand it to chart displays such as hourly forecasts. The chart part is implemented using svg, so that the dark mode style operation can be convenient, so svg is used instead of canvas. The drawing is done directly using native JavaScript in the browser, and only a line chart needs to be drawn. Chart.js is obviously too bloated for this. The original design used a difficult way to insert the chart in the middle, so later I separated the morning data, the chart, and the evening data, because the column width is consistent, so there is no need to worry about misalignment.

Then it's time to draw the chart. First, calculate the X coordinate of each point uniformly, and then determine the Y coordinate of each point based on the temperature. The points are represented by svg circle elements, and the line part is handled directly using path.

The syntax logic of the d parameter of path is actually similar to the logic of canvas drawing. First, use the M (MoveTo) command to move the starting point to the position of the first point, and then use the L (LineTo) command to draw the remaining lines.

4. Dark mode

You can define the card style in dark mode by using the media query @media(prefers-color-scheme: dark).

It is worth mentioning that I used CSS variables, and most browsers already support them, which can greatly reduce duplicate code.

Sometimes users may not want the media query to modify the card style on their own, so I provide the theme attribute to control the card color. You can use theme="light" or theme="dark" to lock the card in light mode or dark mode. I thought of implementing this small feature entirely using CSS. I didn't use Web Components much before, and I thought I could easily handle it with the host selector. So I wrote the following CSS...

:host {
    // Default style
}

:host[theme="dark"] {
    // Dark mode style
}

But... it failed. After searching through MDN, I found this selector (). So the correct way to write it should be like this (so which of the little friends who have flipped through the Blink source code can tell me why it was designed like this...):

:host {
    // Default style
}

:host([theme="dark"]) {
    // Dark mode style
}

Ⅲ. Summary

Writing DW this time has taught me a lot. I rarely do chart generation by myself when writing front-end code. I often just use chartjs or echarts. I also have a more comprehensive understanding of Web Components, and I am also familiar with the use of flex layout~. At least I didn't waste my time watching Xiaomi Weather for two days. My classmates thought I was crazy when they saw me holding my phone and brushing Xiaomi Weather all day~.

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