Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A wrapper component that allows you to utilise P5 sketches within React apps.

License

NotificationsYou must be signed in to change notification settings

P5-wrapper/react

Repository files navigation

@P5-wrapper/react

@P5-wrapper/react

A component to integrateP5.js sketches intoReact apps.

Note: Version 5 is still in development, currently 5.0.0-rc.0 has beenreleased for internal development testing ONLY. It is recommended to continueutilising version 4.4.0 until it is out of therc versioning scheme.

Installation

To install, use the following command in the format appropriate to your chosenpackage manager:

[npm|yarn|pnpm] [install|add] p5 @p5-wrapper/react

Peer dependencies

Please note thatp5,react andreact-dom are peer dependencies, meaningyou should ensure they are installed before installing React P5 Wrapper.

"peerDependencies":{"p5":">= 1.4.1","react":">= 18.2.0","react-dom":">= 18.2.0"},

TypeScript

If you would like to use Typescript, you should installp5 types in thedevelopment environment:

[npm|yarn|pnpm] [install|add] -D @types/p5

Next.js

If you plan to use this component within a Next.js application, you shouldinstead useour Next.js dynamic implementationinstead. To do get started, you can run:

[npm|yarn|pnpm] [install|add] p5 @p5-wrapper/next @p5-wrapper/react

Please continue reading these docs and also look atthe Next.js dynamic implementation docsfor further supporting information.

Demo & Examples

Live demo

A live demo can be viewed atP5-wrapper.github.io/react.

Examples

The repository contains furtherexamples.

To try them out for yourself fork the repository, be sure you havePNPM installed and then run the following:

git clone git@github.com:<your username>/react.gitcd reactpnpm installpnpm preview

Then just openhttp://localhost:3001 in a browser.

Usage

Javascript

import*asReactfrom"react";import{ReactP5Wrapper}from"@p5-wrapper/react";functionsketch(p5){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}exportfunctionApp(){return<ReactP5Wrappersketch={sketch}/>;}

TypeScript

TypeScript sketches can be declared in two different ways, below you will findtwo ways to declare a sketch, both examples do the exact same thing.

In short though, theReactP5Wrapper component requires you to pass asketchprop. Thesketch prop is simply a function which takes ap5 instance as it'sfirst and only argument.

Option 1: Declaring a sketch using theP5CanvasInstance type

import*asReactfrom"react";import{P5CanvasInstance,ReactP5Wrapper}from"@p5-wrapper/react";functionsketch(p5:P5CanvasInstance){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}exportfunctionApp(){return<ReactP5Wrappersketch={sketch}/>;}

Option 2: Declaring a sketch using theSketch type

Using theSketch type has one nice benefit over usingP5CanvasInstance andthat is that thep5 argument passed to the sketch function is auto-typed as aP5CanvasInstance for you.

Side note:

In general, it comes down to personal preference as to how you declare yoursketches and there is nothing wrong with using theP5CanvasInstance manuallyin a regularfunction declaration.

import*asReactfrom"react";import{ReactP5Wrapper,Sketch}from"@p5-wrapper/react";constsketch:Sketch=p5=>{p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};};exportfunctionApp(){return<ReactP5Wrappersketch={sketch}/>;}

TypeScript Generics

We also support the use of Generics to add type definitions for your props. Ifused, the props will be properly typed when the props are passed to theupdateWithProps method.

To utilise generics you can use one of two methods. In both of the examplesbelow, we create a custom internal type calledMySketchProps which is a uniontype ofSketchProps and a custom type which has arotation key applied toit.

Side note:

We could also write theMySketchProps type as an interface to do exactly thesame thing if that is to your personal preference:

interfaceMySketchPropsextendsSketchProps{rotation:number;}

This means, in these examples, that when therotation prop that is provided aspart of theprops passed to theupdateWithProps function, it will becorrectly typed as anumber.

Usage with theP5CanvasInstance type
import{P5CanvasInstance,ReactP5Wrapper,SketchProps}from"@p5-wrapper/react";importReact,{useEffect,useState}from"react";typeMySketchProps=SketchProps&{rotation:number;};functionsketch(p5:P5CanvasInstance<MySketchProps>){letrotation=0;p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.updateWithProps=props=>{if(props.rotation){rotation=(props.rotation*Math.PI)/180;}};p5.draw=()=>{p5.background(100);p5.normalMaterial();p5.noStroke();p5.push();p5.rotateY(rotation);p5.box(100);p5.pop();};}exportfunctionApp(){const[rotation,setRotation]=useState(0);useEffect(()=>{constinterval=setInterval(()=>setRotation(rotation=>rotation+100),100);return()=>{clearInterval(interval);};},[]);return<ReactP5Wrappersketch={sketch}rotation={rotation}/>;}
Usage with theSketch type
import{ReactP5Wrapper,Sketch,SketchProps}from"@p5-wrapper/react";importReact,{useEffect,useState}from"react";typeMySketchProps=SketchProps&{rotation:number;};constsketch:Sketch<MySketchProps>=p5=>{letrotation=0;p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.updateWithProps=props=>{if(props.rotation){rotation=(props.rotation*Math.PI)/180;}};p5.draw=()=>{p5.background(100);p5.normalMaterial();p5.noStroke();p5.push();p5.rotateY(rotation);p5.box(100);p5.pop();};};exportfunctionApp(){const[rotation,setRotation]=useState(0);useEffect(()=>{constinterval=setInterval(()=>setRotation(rotation=>rotation+100),100);return()=>{clearInterval(interval);};},[]);return<ReactP5Wrappersketch={sketch}rotation={rotation}/>;}

Using abstracted setup and draw functions

import*asReactfrom"react";import{ReactP5Wrapper}from"@p5-wrapper/react";functionsetup(p5){return()=>{p5.createCanvas(600,400,p5.WEBGL);};}functiondraw(p5){return()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}functionsketch(p5){p5.setup=setup(p5);p5.draw=draw(p5);}exportfunctionApp(){return<ReactP5Wrappersketch={sketch}/>;}

Props

The only required property of theReactP5Wrapper component is thesketchprop. Thesketch prop is a function that will be passed a p5 instance to usefor rendering your sketches as shown inthe usage section above.

You can pass as many custom props as you want to theReactP5Wrapper componentand these will all be passed into theupdateWithProps method if you havedefined it within your sketch.

Reacting to props

In the below example you see theupdateWithProps method being used. This iscalled when the component initially renders and when the props passed to thewrapper are changed, if it is set within your sketch. This way we can render ourReactP5Wrapper component and react to component prop changes directly withinour sketches!

import{ReactP5Wrapper}from"@p5-wrapper/react";importReact,{useEffect,useState}from"react";functionsketch(p5){letrotation=0;p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.updateWithProps=props=>{if(props.rotation){rotation=(props.rotation*Math.PI)/180;}};p5.draw=()=>{p5.background(100);p5.normalMaterial();p5.noStroke();p5.push();p5.rotateY(rotation);p5.box(100);p5.pop();};}exportfunctionApp(){const[rotation,setRotation]=useState(0);useEffect(()=>{constinterval=setInterval(()=>setRotation(rotation=>rotation+100),100);return()=>{clearInterval(interval);};},[]);return<ReactP5Wrappersketch={sketch}rotation={rotation}/>;}

Children

To render a component on top of the sketch, you can add it as a child of theReactP5Wrapper component and then use the exportedP5WrapperClassNameconstant in your css-in-js library of choice to style one element above theother via css.

For instance, usingstyled components, we couldcenter some text on top of our sketch like so:

import{P5WrapperClassName,ReactP5Wrapper}from"@p5-wrapper/react";importstyled,{createGlobalStyle}from"styled-components";constGlobalWrapperStyles=createGlobalStyle`  .${P5WrapperClassName} {    position: relative;  }`;constStyledCentredText=styled.span`  .${P5WrapperClassName} & {    position: absolute;    top: 50%;    left: 50%;    transform: translate(-50%, -50%);    color: white;    font-size: 2rem;    margin: 0;    text-align: center;  }`;exportfunctionApp(){const[rotation,setRotation]=useState(0);useEffect(()=>{constinterval=setInterval(()=>setRotation(rotation=>rotation+100),100);return()=>{clearInterval(interval);};},[]);return(<Fragment><GlobalWrapperStyles/><ReactP5Wrappersketch={sketch}rotation={rotation}><StyledCentredText>Hello world!</StyledCentredText></ReactP5Wrapper></Fragment>);}

Of course, you can also use any other css-in-js library or by just using simplecss to achieve almost anything you can imagine just by using the wrapper classas your root selector.

Fallback UIs

Lets say you want to have a fallback UI in case thesketch ever falls out ofsync or is undefined for some reason. If this is a use case for you then youcall use thefallback prop to provide the necessary UI to show in the casethat thesketch becomes undefined. An example could be as follows:

import*asReactfrom"react";import{ReactP5Wrapper}from"@p5-wrapper/react";functionsketchOne(p5){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}functionsketchTwo(p5){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(500);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}exportfunctionApp(){const[sketch,setSketch]=React.useState(undefined);constchooseNothing=()=>setSketch(undefined);constchooseSketchOne=()=>setSketch(sketchOne);constchooseSketchTwo=()=>setSketch(sketchTwo);return(<><ul><li><buttononClick={chooseNothing}>Choose nothing</button></li><li><buttononClick={chooseSketchOne}>Choose sketch 1</button></li><li><buttononClick={chooseSketchTwo}>Choose sketch 2</button></li></ul><ReactP5Wrapperfallback={<h1>No sketch selected yet.</h1>}sketch={sketch}/></>);}

In this case, by default the fallback UI containing<h1>No sketch selected yet.</h1> will be rendered, then if you select asketch, it will be rendered until you choose to once again "show nothing" whichfalls back to the fallback UI.

Error and Loading UIs

Since version 4.4.0, it was possible to add afallback prop, see the sectionon fallbacks.

Since version 5 it is now possible to pass anerror andloading prop to thewrapper which allow the user to pass different UIs for error and loading states.

  • Theerror state will trigger if the sketch or the wrapper encounter anissue, otherwise a default error view will be shown.
  • Theloading state will trigger while the wrapper is being lazy loaded,otherwise a default loading view will be shown.

Error UIs

To show a custom UI when an error occurs within the sketch or the wrapper, youcan pass a lazy function to theerror prop.

import*asReactfrom"react";import{P5CanvasInstance,ReactP5Wrapper}from"@p5-wrapper/react";// This child will throw an error, oh no!functionErrorChild(){thrownewError("oops");}// This view will catch the thrown error and give you access to what exactly was thrown.functionErrorUI(error:any){if(errorinstanceofError){return<p>An error occured:{error.message}</p>;}return<p>An unknown error occured:{error.toString()}</p>;}functionsketch(p5:P5CanvasInstance){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}exportfunctionApp(){return(<ReactP5Wrappersketch={sketch}error={ErrorUI}><ErrorChild/></ReactP5Wrapper>);}

Instead of the sketch, this will render<p>An error occured: oops</p>. Notethat in truth, theErrorView willalways receiveany values since JS /TS allow you tothrow whatever values you want to, this is why we have to addtheerror instanceof Error check to be sure the value we got was actually anError instance and not some other value like anumber,string,object oranything else that could be thrown by JS / TS.

As mentioned above, theerror state will trigger if the sketch or the wrapperencounter an issue, otherwise a default error view will be shown.

Loading UIs

To show a custom UI while the sketch UI is being lazy loaded, you can pass alazy function to theloading prop.

import*asReactfrom"react";import{P5CanvasInstance,ReactP5Wrapper}from"@p5-wrapper/react";functionLoadingUI(){return<p>The sketch is being loaded.</p>;}functionsketch(p5:P5CanvasInstance){p5.setup=()=>p5.createCanvas(600,400,p5.WEBGL);p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};}exportfunctionApp(){return<ReactP5Wrappersketch={sketch}loading={LoadingUI}/>;}

In the initial period between the sketch render starting and it's lazy loadingending, theLoadingUI will be shown!

As mentioned above, theloading state will trigger while the wrapper is beinglazy loaded, otherwise a default loading view will be shown.

P5 plugins and constructors

As discussed in multiple issues such as#11,#23,#61 and#62, there seems to beconfusion as to how we can use P5 plugins and constructors out of the box. Thissection aims to clarify these!

Plugins

Since P5 is being used inP5 instance modeas part of this project, P5 will not automatically load global plugins like itusually might in global mode.

Let's say we want to use theP5 sound plugin in ourcomponent, we could do the following:

import*asp5from"p5";import{ReactP5Wrapper,Sketch}from"@p5-wrapper/react";importReact,{useEffect,useState}from"react";(windowasany).p5=p5;awaitimport("p5/lib/addons/p5.sound");constsketch:Sketch=p5=>{letsong:p5.SoundFile;letbutton:p5.Element;p5.setup=()=>{p5.createCanvas(600,400,p5.WEBGL);p5.background(255,0,0);button=p5.createButton("Toggle audio");button.mousePressed(()=>{if(!song){constsongPath="/piano.mp3";song=p5.loadSound(songPath,()=>{song.play();},()=>{console.error(`Could not load the requested sound file${songPath}`);});return;}if(!song.isPlaying()){song.play();return;}song.pause();});};p5.draw=()=>{p5.background(250);p5.normalMaterial();p5.push();p5.rotateZ(p5.frameCount*0.01);p5.rotateX(p5.frameCount*0.01);p5.rotateY(p5.frameCount*0.01);p5.plane(100);p5.pop();};};exportdefaultfunctionApp(){return<ReactP5Wrappersketch={sketch}/>;}

In this Typescript + React example, we can see a few key things.

  • Firstly we need to setp5 on thewindow object manually. This is becausep5.sound requires that it be executed client side only AND thatp5 beavailable BEFORE it is imported into the global (window) scope.
  • Secondly, we ensure that audio is played after a user action, in our case thishappens on a button click. This is because in some browsers, without waitingfor a user interaction before playing audio, the audio will be blocked by thebrowser from playing at all.
  • Thirdly and relevant especially to Safari users, Safari blocks audio from alltabs by default, you will need to manually change this setting in your Safarisettings. This could affect other browsers but sadly this is a browserdecision and untilP5 Sound isupdated to support newer audio APIs and browser requirements. This couldhappen at anytime in other places and is aP5 Sound issue most generallybecause it does not ask for permissions by default, even though browsers havebeen requiring it for some time.

Note: The above example requires support fortop level await,dynamic import statements andthe stream API to be supported in your browser.Furthermore,the stream API built into thebrowser requires that HTTPS is used to ensure secure data transmission.

Constructors

To access P5 constructors such asp5.Vector orp5.Envelope, you need to usethe instance mode syntax instead. For example:

ConstructorGlobal mode accessorInstance mode accessor
Vectorp5.Vectorp5.constructor.Vector
Envelopep5.Envelopep5.constructor.Envelope

So now that we know this, let's imagine we want a random 2D Vector instance. Inoursketch function we would simply callp5.constructor.Vector.random2D()instead ofp5.Vector.random2D(). This is because of how theP5 instance modewas implemented by the P5 team. While I am not sure why they decided to changethe API for instance mode specifically, it is still quite simple to use theconstructs we are used to without much extra work involved.

Development

NOTE: The source code for the component is in thesrc directory.

To build, watch and serve the examples which will also watch the componentsource, run:

  pnpm preview

[8]ページ先頭

©2009-2025 Movatter.jp