profile picture

Chi Kin Tsang

Front-end Developer

Learn more about me.

Utrecht, The Netherlands

blog

25
Aug

Loading map...

is at

Restaurant Juliana

Dim Sum

Only one of the two spots in Utrecht where you can find traditional Hong Kong dim sum. A great amount of food for a fair price. The best part of dim sum is sharing the dishes and being able to get a taste of each and every one of them.

Loading images...

21
Aug

A New Journey

On September 15th, I’ll be starting a new journey as a developer at Enrise! Thanks, my future colleagues of Team Craft, for the warm welcome today.

September 15th can’t come soon enough!

Loading images...

19
Aug

Loading map...

is at

Japanse Tuin, Maximapark

Maximapark

It looks like the renovations to the Japanese Gardens in the Maximapark have been completed. The Japanese style bridges and pavillion they’ve added look absolutely stunning! It probably would look even better during spring with all the flowers around.

Loading images...

16
Aug

Testing the Elbow

I’ve just completed my first upper body workout in ages to see how the tennis elbow holds up after exercising.

1 minute rest per exercise and 3 minutes of rest between sets.

(21 Aug) Edit: I’m definitely still feeling the lingering effect of the workout. I’ll try to reduce the load in my next session to see if it has any effect.

1

1

10 reps

+ 8 kg

2

10 reps

+ 8 kg

3

10 reps

+ 8 kg

2

1

10 reps

2

10 reps

3

10 reps

3

1

10 reps

+ 8 kg

2

10 reps

+ 8 kg

3

10 reps

+ 8 kg

4

1

10 reps

+ 4.5 kg

2

10 reps

+ 4.5 kg

3

10 reps

+ 4.5 kg

5

1

10 reps

2

10 reps

3

10 reps

24
Jul

Infinite Marquee using React and Motion One

I'm sure you have seen them before. A continuous scrolling and looping animation, containing either text or images called a marquee. It's often used to show the logos of partners or as a picture gallery. Let's recreate this animation using React and Motion One!

Initializing the project

First, we will be creating our project and installing some packages.

  • Let's start with creating our app using NextJS by running npx create-next-app@latest in the terminal.
  • Next, for handling the animations we'll install the Motion library by running npm i motion.

Creating the layout

To create the layout, let's start with creating a list of images which we'll be using for the project.

Then we map over the list of images and return an image component to render those images.

  • The width of the images is calculated by dividing the width of the container (in this case the width of the viewport) by the amount of images in the list with logos.length.
  • We use flex-shrink-0 to prevent the images from shrinking and causing it to overlap with each other.

It should look like this:

Loading image...

The static marquee layout

Animating the Infinite Scrolling Marquee

To create the animation, we have to start with importing the motion component from the Motion One library.

Then we change the <div> element containing the images to a <motion.div> component. To make the images move horizontally, we have to animate the x-axis of the motion component. At last, we make it loop continuously at a steady pace by setting the ease to linear and repeat it endlessly with repeat: Infinity.

  • The animation starts off at x = "0" which is the default position and moves left until it reaches -100%, which means it'll move left until its entire width has moved out of the container.

Loading image...

An overview of the layout at the start and end of the animation. Do you spot a problem?

As you can see there is a big empty space left behind from the logos leaving the container when x reaches -100%. To fix this we have to duplicate our list of logos to fill in the empty gap.

This works, because the width is exactly double the size of the original list of logos.

Loading image...

The duplicate images will fill in the empty space

Let's put it all together and create the component.

It will look like this:

We can add a blur to the sides of the marquee for a subtle fade effect.

The end result will look like this:

7
Jul

30
Jun

Kimmade

Nothing goes above a refreshing Vietnamese meal after one of the hottest days of the year.

Loading images...

22
Jun

Scroll Parallax using React and Motion One

Recently, I've been seeing this parallax effect being used quite often on Wordpress websites. Let's recreate this subtle parallax effect using React and Motion One.

Initializing the project

First, we will be creating our project and installing some packages.

  • Let's start with creating our app using NextJS by running npx create-next-app@latest in the terminal.
  • Next, for handling the animations we'll install the Motion library by running npm i motion.
  • We will be using Lenis scroll to enable smooth scroll animations by running npm i lenis.

Creating the layout

We create a list of images which we'll be using for the project.

Next we render the images from the list.

Animating the Parallax Image

First, we create a ref for the container of the image. We'll set it as the target for the useScroll hook from Motion to track its vertical progress within the viewport.

Next, we specify the offset. It describes the points in which the target intersects with the viewport.

  • "start end" means the intersection in which the top ("start") of the target element meets the bottom ("end") of the viewport. It describes the place where the element first enters the screen.
  • "end start" means the intersection in which the bottom ("end") of the target meets the top ("start") of the viewport. It's the place where the target element leaves the screen.
  • The scrollYProgress value ranges from 0 to 1 with "0" being the moment the target element first enters the viewport and "1" the moment right before the element leaves the viewport.

With the useTransform hook from Motion we can transform the scrollYProgress values into other motion values.

  • The value for imageY will be -250 pixels when the value for scrollYProgress is at "0" and 0 pixels when the value of scrollYProgress is at "1". The hook will take care of all the values in between these two numbers. This means the image will be shifted down by -250 pixels when it first enters the viewport and it will move up as the user continues to scroll down.
  • We do the opposite for textY to create a second layer for the parallax effect.

Let's put it all together and create the component.

  • To apply the animation to an element, we have to change it to a motion component. <img> turns into <motion.img> and <h1> turns into <motion.h1>. We can then access the style and customize it with our motion value.

The end result will look like this:

15
Jun

Increasing the Reps

1

2

1

25 reps

+ 2 kg

2

25 reps

+ 2 kg

3

25 reps

+ 2 kg

4

25 reps

+ 2 kg

3

1

25 reps

+ 2 kg

2

25 reps

+ 2 kg

3

25 reps

+ 2 kg

4

25 reps

+ 2 kg

4

1

25 reps

+ 2 kg

2

25 reps

+ 2 kg

3

25 reps

+ 2 kg

4

25 reps

+ 2 kg

5

1
Jun

Creating Custom Rich-Text Components using TinaCMS

Tina's rich-text editor comes out of the box with a lot of useful options to spice up the blog posts, like inserting code blocks and adding images to the post. I've been wanting to add MP4 videos to my posts, but it's lacking that feature by default. Luckily, it's easy to extend it with custom rich-text components. Here's how to do it.

Creating the component

Let's start with creating the MP4 video player component using React.

  • To make the videos automatically play, it is required for them to be muted.
  • playsInline is needed to make it play automatically in mobile browsers, because the default behaviour is to have the video be paused until the user plays the video, which opens it fullscreen.
  • Add controls for accessibility to give the user the option to pause the video.

Next, to extend the rich-text editor, you have to add the custom component to the TinaMarkdown component which renders the body.

Finally, add it to Tina's config.ts file as a template.

  • Use type: "image" to make use of Tina's image field for handling the media selection and uploads.

Now you should be able to embed MP4 videos to your blog posts:

Loading image...