(Jump to Code | Demo) Have you seen the chat heads created by facebook messenger? They are stacked over each other and when you drag top one, the other heads follow. But this happens with leaving a trail. So, it looks like flash is running fast. In this article we are going to implement facebook messenger chat heads using React Spring library.

Let me give you a brief introduction of React Spring. It is a library which can help you in creating your animations. It is simple, powerful and very optimized. For almost all the general use cases it has sufficient support.

To get a hairline idea of library, you may have a look at this article –

Code Example – Facebook messenger chat heads using React spring

First we will make an array of images that we are going to use in chat heads. Let’s call this file, imgs.js.

Code for imgs.js

export default [
  'https://randomuser.me/api/portraits/med/women/22.jpg',
  'https://randomuser.me/api/portraits/med/men/25.jpg',
  'https://randomuser.me/api/portraits/med/men/18.jpg',
  'https://randomuser.me/api/portraits/med/women/55.jpg',
  'https://randomuser.me/api/portraits/med/men/32.jpg',
  'https://randomuser.me/api/portraits/med/women/28.jpg',
  'https://randomuser.me/api/portraits/med/women/73.jpg'
]

These are 7 fake images generated by randomuser.me.

Code for index.js

This is the main file where all the code is going to be. So, we will break it into parts and understand it.

Let’s start with creating a single chat head

<animated.div
>
  <div
	style={{
	  backgroundImage: `url(/*image url*/)`
	}}
  />
</animated.div>

We need to style it a bit so that it can look like a chat head of messenger. Create a stylesheet with name styles.css. Code for stylesheet is

body,
html {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir;
  background: #f0f0f0;
  height: 100vh;
}

* {
  box-sizing: border-box;
  user-select: none;
  -moz-user-select: none;
}

#root {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100vw;
}

#root > div {
  position: absolute;
  width: 80px;
  height: 80px;
  background: grey;
  border-radius: 50%;
  box-shadow: 0px 10px 30px -5px rgba(0, 0, 0, 0.3);
  transition: box-shadow 0.5s, opacity 0.5s;
  will-change: transform;
  cursor: grab;
  overflow: hidden;
  touch-action: none;
  will-change: transform;
}

#root > div > * {
  height: 100%;
  background-size: cover;
  background-position: center center;
  margin: 0vw 0;
}

#root > div.dragging {
  cursor: grabbing;
}

#root > div.disabled {
  opacity: 0.5;
  cursor: default;
}

#root > div:hover {
  box-shadow: 0px 30px 100px -10px rgba(0, 0, 0, 0.4);
}

button {
  position: absolute;
  top: 20px;
  left: 20px;
}

With the help of this css, the images will become circular and there will be shadow on them. Also they will form a stack due to absolute positioning.

Let’s see the entire index.js file –

import React from 'react'
import ReactDOM from 'react-dom'
import { useTrail, animated } from 'react-spring'
import { useGesture } from 'react-use-gesture'
import imgs from './imgs'
import './styles.css'

function Card() {
  const domTarget = React.useRef(null)

  const trans = (x, y) => `translate3d(${x}px,${y}px,0) translate3d(-50%,-50%,0)`
  const [trail, set] = useTrail(7, () => ({ xy: [0, 0], config: (i) => ({ tension: 1200, friction: 80 }) }))

  const [drag, setDrag] = React.useState(false)

  useGesture(
    {
      onDragStart: () => setDrag(true),
      onDrag: ({ offset: [x, y] }) => set({ xy: [x, y] }),
      onDragEnd: () => setDrag(false)
    },
    { domTarget, eventOptions: { passive: false } }
  )

  return trail.map((props, i) => (
    <animated.div
      key={i}
      ref={i === 0 ? domTarget : null}
      className={`${drag ? 'dragging' : ''}`}
      style={{ transform: props.xy.to(trans), zIndex: 10 - i }}
    >
      <div
        style={{
          backgroundImage: `url(${imgs[i]})`
        }}
      />
    </animated.div>
  ))
}

ReactDOM.render(<Card />, document.getElementById('root'))

    Tweet this to help others

For creating draggable facebook messenger kind of chat heads (with trails), we will need react-spring and react-use-gesture.

  • React Spring – It is used for animation.
  • React use gesture – It is used for implementing dragging functionality.

You should know few things about our process –

  1. Only the top image on the stack of chat heads is draggable. Others will just follow the first.
  2. We need to set the z-index in decreasing order. Otherwise we will get an unexpected output.

In the code, we are first defining a reference variable, domTarget. We will use it to refer the first chat head for dragging purpose. If you want to learn more about reference variables then have a look at below article –

For creating a trail animation, React spring provides useTrail() hook. We need to provide the total number of trail animations as first parameter. We have 7 images so we will create 7 animations. Second parameter is the function which returns CSS transition values.

To create draggable feature, we are using useGesture hook from react-use-gesture. This will set the transition values according to dragging.

Next, we are creating chat heads by looping over all the 7 trails. Rest, the library will handle.

Live Demo

You may also like –

Comments

About the Author

akamit

I am Akash Mittal, an overall computer scientist. If you want to guest post, need help in your projects, want to advertise, Feel free to contact me at [email protected]

View All Articles