Skip to content

johnrjj/react-measure-text-worker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

48 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“ react-measure-text-worker πŸ”¨

An experimental React component that performs expensive text measurements off the main thread and return results to the component's children.

Demo

Background

Problem

Require ability to dynamically measure text for width, height, and other style attributes off-thread without interacting with DOM.

Concepts

Three main concepts:

  1. WebWorker work is done off the main UI thread.
  2. With the introduction of OffscreenCanvas in Chrome 69, we can now perform Canvas work in a WebWorker.
  3. Inside a Canvas, you can do some powerful calculations on styles and positioning.

Let's combine these three concepts in the context of an application that does significant DOM or Canvas measuring. We can move these expensive calculations off the main thread by performing the same calculations inside a Canvas on a separate thread via a WebWorker. This will allow the main thread (UI thread) to free up resources, and allow the main thread to run smoothly and maintain a desired 60fps.

More details on OffscreenCanvas with WebWorkers can be found here

Solution - Declarative Approach

<MeasureTextOffThread/>

A React component that is able to handle expensive measurments of text attributes off-thread in a WebWorker, which then returns the requested measurements to the main thread for handing off to child components.

Getting Started

Clone the repo, install the dependencies, and run the dev environment. That's it!

git clone https://github.com/johnrjj/react-measure-text-worker.git
yarn install
yarn start

Navigate to localhost:1234 to see the app running

API

MeasureTextOffThread renders a "child as a function" pattern which provides a textData object to the children, which the children can then use to render however they would like.

import { MeasureTextOffThread } from 'react-measure-text-off-thread';

const text = 'measure this text';

// inside render...
<MeasureTextOffThread
  text={text}
  fontSize={20}
  fontFamily={'Arial'}
>
{textData => (
  <span>
    <div style={{ width: textData.width, height: textdata.height }}>
    	Do overlay here
    </div>
    <span>{text}</span>
  </span>
)}
</MeasureTextOffThread>

Architecture

       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                                                                     
       β”‚           β”‚                                                                                     
       β”‚    App    β”‚                     thread                                                          
       β”‚           β”‚                    boundary                                                         
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                                                     
             β”‚                              β”‚                                                            
             β”‚                                                                                           
             β”‚                              β”‚                                                            
             β”‚                postMessage         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                    
             β–Ό                request to    β”‚     β”‚                 β”‚                                    
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    webworker          β”‚    WebWorker    β”‚                                    
β”‚                         │─────────────────┼────▢│    onmessage    │────────────────────┐               
β”‚ </MeasureTextOffThread> │◀────────┐             β”‚                 β”‚                    β–Ό               
β”‚                         β”‚         β”‚       β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚                                          β”‚                  β”‚     β”‚
             β”‚                      β”‚       β”‚                                  β”‚ Worker processes β”‚ LRU β”‚
             β”‚                      β”‚                                          β”‚      request     β”‚cacheβ”‚
             β”‚                      β”‚       β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚                  β”‚     β”‚
    ({ width, height })             β”‚          β”‚                           β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜
             β”‚                      β”‚       β”‚  β”‚ postMessage Response Bus  β”‚             β”‚               
             β”‚                      └─────────◀│         (shared)          β”‚β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               
             β–Ό                              β”‚  β”‚                           β”‚       send response         
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      payload to bus         
β”‚                         β”‚                 β”‚                                                            
β”‚        children         β”‚                                                                              
β”‚                         β”‚                 β”‚                                                            
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                                                              

Performance

πŸ”₯ It's really fast.