We will build a product gallery page together during this tutorial. This tutorial does not assume any existing Lynx knowledge. The techniques you'll learn in the tutorial are fundamental to building any Lynx pages and applications.
This tutorial is designed for people who prefer to learn by doing and want to quickly try making something tangible. If you prefer learning each concept step by step, start with Describing the UI.
Let's first have a look at the result! To see the page live, download and install LynxExplorer on your device, then scan the generated QR code below.
Check out our detailed quick start doc that will guide you through creating a new Lynx project.
You may notice that the project is using TypeScript. Although Lynx and ReactLynx support both TypeScript and plain JavaScript, we recommend TypeScript for a better development experience, provided by static type checking and better editor IntelliSense.
Since the focus of this tutorial is not on how to style your UI, you may just save some time and directly copy the below index.css
file:
and import it as a global styles:
This make sure your UI look great when you are following this tutorial.
Lynx supports a wide variaties of styling features, including global styles, CSS Modules, inline styles, Sass, CSS variables, and more! Please refer to Rspeedy - Styling page for how to pick your best styling configurations.
Now, let's start by creating the first image card, which will be the main part of this page.
Great, you can now see the image card displayed. Here, we use the <image>
element to display your image. You only need to give it a width and height (or specify the aspectRatio property as shown here), and it will automatically resize to fit the specified dimensions.
This component can receive a picture property, allowing you to change the image it displays. In fact, all components can receive external inputs like this, giving you control over them.
The Lynx <image>
element can accept a local relative path as the src
attribute to render an image, which is the most important attribute of the <image>
element. All images in this page are sourced locally, and these paths need to be imported before use.
However, if your images are stored online, you can easily replace them with web image addresses by changing the value of the src attribute to the corresponding web image link.
We can add a small white heart in the upper right corner and make it the like button for the image card. Here, we implement a small component called LikeIcon
:
We want each card to know whether it has been liked, so we added isLiked, which is its internal data. It can use this internal data to save your changes.
Then we add the bindtap event to <image>
, so that when the user clicks the heart, it triggers this event and changes the state of isLiked
:
If you come from a web development background, you might be more familiar with naming conventions like onclick (HTML attribute) or onClick (in the React community). Lynx follows a different convention: due to the static nature of its architecture, it uses bind*
and catch*
. Learn more on the Event Handling page.
Finally, we use isLiked
to control the like effect. Because isLiked is a state, LikeIcon
will respond to its changes, turning into a red like icon, and the <view>
used to render the animation effect will be conditionally rendered:
To give this like a better visual interaction effect, we added animations, which are all in index.scss. You can also learn more about animations in the Animation section. Then replace it with your preferred style!
<list>
To show all your beautiful images, you may need help from <list>
. This way, you will get a scrollable page that displays a large number of similar images:
Each child component of <list>
needs to be <list-item>
, and you must specify a unique and non-repeating key and item-key attribute, otherwise it may not render correctly.
Of course, we also provide other scrolling elements, such as <scroll-view>
, to achieve similar effects. Here, we use a waterfall layout as the child node layout option. <list>
also accepts other layout types, which you can refer to in list.
You can refer to this Scrolling documentation to learn more about scrolling and scrolling elements.
If you want to create a desktop photo wall, you need to add an auto-scroll feature to this page. Your images will be slowly and automatically scrolled, allowing you to easily see more images:
We use the useEffect
hook to call the autoScroll
method.
In Lynx, all native elements have a set of "methods" that can be called via their ref. Unlike on the web, this call is asynchronous, similar to message passing. You need to use invoke with the method name method and parameters param to call them.
Like most apps, we can add a scrollbar to this page to indicate how many images are left to be displayed. But we can do more! For example, we can replace the default progress bar of <list>
with our preferred style:
Similar to the bindtap
event used to add the like functionality, we add the bindscroll event to <list>
, which will be triggered when the <list>
element scrolls.
The NiceScrollbar component provides an internal method adjustScrollbar, which we call to adjust the scrollbar's position whenever the bindscroll event is triggered.
We use many React techniques in this component, such as forwardRef
and useImperativeHandle
for calling the adjustScrollbar
method. If you are not familiar with them, you can refer to the React official documentation to better understand them.
We use globalProps in this method, where you can use screenHeight
and screenWidth
to get the screen height and width.
You may have noticed this attribute estimated-main-axis-size-px. This attribute can estimate the size of elements on the main axis when they are not yet rendered in <list>
. This is very useful when we add a scrollbar, as we need to know how long the scrollbar needs to be to cover all elements.
Of course, <list>
also supports automatic layout. You can remove this attribute and see the effect—your scrollbar will automatically adjust its length as the elements change from preset height to actual height.
We provide a utility method to estimate the size of the image on the main axis based on the current <list>
layout information and the image dimensions:
At this point, we have a complete page! But you may have noticed that the scrollbar we added still lags a bit during scrolling, not as responsive as it could be. This is because our adjustments are still happening on the background thread, not the main thread that responds to touch scrolling.
The biggest feature of Lynx is its dual-thread architecture. You can find a more detailed introduction in JavaScript Runtime.
To optimize the performance of the scrollbar, we need to introduce Main Thread Script (MTS) to handle events on the main thread, migrating the adjustments we made in the previous step for the scrollbar's height and position from the background thread to the main thread.
To let you see the comparison more clearly, we keep both scrollbars:
Now you should be able to see that the scrollbar on the left, controlled with main thread scripting, is smoother and more responsive compared to the scrollbar on the right that we implemented earlier. If you encounter issues in other UIs where updates need to happen immediately, try this method.
We also provide another tutorial, guiding you through a deep dive into implementing a highly responsive swiper in Tutorial:Product Detail.
We remove the redundant scrollbar used for comparison, and our Gallery is now complete! Let's take a look at the final result:
Configurations! You have successfully created a product gallery page! 🎉 Throughout this tutorial, you’ve covered the basics of writing interactive UIs on the Lynx platform and some of the differences between using it on the Web.