Package Exports
- cdejs
Readme
CanvasDotEffect
CanvasDotEffect is a lightweight JS library that helps create customizable and interactive dot-based effects using the Canvas API.
Table of Contents
- Getting Started / Minimal setup
- Classes
- Execution order
- Optimization
- Intended practices
- React Component Template
- Credits
Getting Started / Minimal setup
Either run: npx cdejs-template yourFolderName
or follow these couple steps! (↓)
- Get the library file. (
npm install cdejs
or canvasDotEffect.min.js)
<!-- Only if you're using the browser version! Otherwise use: import {...} from "cdejs" -->
<script src="canvasDotEffect.min.js"></script>
- In your HTML file, place a canvas element. The canvas will later automatically take the size of its parent element.
<div class="canvasHolder">
<canvas id="canvasId"></canvas>
</div>
- In a JS file, create a new Canvas instance with the HTML canvas element in parameter.
const CVS = new Canvas(document.getElementById("canvasId"))
- From this Canvas instance, add any canvas objects you want.
// Create a canvas object
const dummyShape = new Shape([50, 50], [new Dot()])
// Add it to the canvas
CVS.add(dummyShape)
- Set the mouse event listeners for mouse interactions.
// Set up the prebuilt event listeners, allowing the creation of more interactive effects!
CVS.setMouseMove(/*custom callback*/)
CVS.setMouseLeave()
CVS.setMouseDown()
CVS.setMouseUp()
- Once everything is created and ready to go, start the drawing loop!
// Start
CVS.startLoop()
- In the end, you should have something like this:
const CVS = new Canvas(document.getElementById("canvasId"))
// Creating and adding shapes ...
const dummyShape = new Shape([50, 50], [new Dot()])
CVS.add(dummyShape)
CVS.setMouseMove(/*custom callback*/)
CVS.setMouseLeave()
CVS.setMouseDown()
CVS.setMouseUp()
CVS.startLoop()
Note: if you are using de NPM version of this librairy, using Vite or any other bundler is recommended.
- Minimal example package.json
{
"name": "my_project",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"dependencies": {
"cdejs": "^1.0.16"
},
"devDependencies": {
"vite": "^6.2.2"
}
}
Class descriptions
The following sections are short documentations of each class, basically what it does and what are the most important aspects of it.
Canvas
The Canvas class is the core of the project. It manages the main loop, the window listeners, the delta time, the HTML canvas element, all the canvas objects, and much more.
The Canvas constructor takes the following parameters:
- new Canvas(cvs, loopingCB, fpsLimit, visibilityChangeCB, cvsFrame, settings, willReadFrequently)
- cvs -> The HTML canvas element to link to.
- loopingCB? -> A callback ran each frame.
(Canvas)=>
- fpsLimit? -> The maximum fps cap. Defaults to V-Sync.
- visibilityChangeCB? -> A callback called on document visibility change.
(isVisible, cvs, event)=>
- cvsFrame? -> If you don't want the canvas to take the size of its direct parent, you can provide another custom HTML element here.
- settings? -> The custom canvas settings (leave
null
for prebuilt default settings). - willReadFrequently? -> If
true
, optimizes the canvas context for frequent readings. (Defaults tofalse
)
To add objects to the canvas, use the add() function:
- add(objs, inactive=false)
// For a source object
CVS.add(yourShape)
// For a prefab or inactive shape
CVS.add(yourShape, true)
To set up mouse/keyboard listeners for the canvas, use the following prebuilt functions:
// Set the important mouse events
CVS.setMouseMove(/*possible custom callback*/)
CVS.setMouseLeave()
CVS.setMouseDown()
CVS.setMouseUp()
// Set the important keyboard events
CVS.setKeyDown(/*possible custom callback*/)
CVS.setKeyUp()
To control the canvas loop, use the following functions:
// Starts the main loop
CVS.startLoop()
// Stops the main loop
CVS.stopLoop()
Example use:
- Creating a Canvas instance that displays fps
// Creating a FPSCounter instance
const fpsCounter = new CDEUtils.FPSCounter()
const CVS = new Canvas(
document.getElementById("canvas"), // The HTML canvas element to link to
()=>{ // Custom callback that runs every frame
// Get current fps
const fps = fpsCounter.getFps()
// Display fps in another element
document.getElementById("fpsDisplay").textContent = fps
}
)
_Obj
The _Obj class is the template class of any canvas object. It should not be directly instantiated.
All canvas objects will have at least these attributes:
- id -> Id of the object.
- initPos -> Initial pos declaration. Can either be a pos array
[x, y]
or a callback(Canvas, obj)=>{... return [x, y]}
- pos -> Array containing the
[x, y]
position of the object. - initRadius -> Initial radius declaration. Can either be a number or a callback
(parent or obj)=>{... return radiusValue}
- radius -> The radius in px object the dot (Or the radius of its dots if is a Shape).
- initColor -> Initial color declaration. Can either be a color value (see ↓) or a callback
(render, obj)=>{... return colorValue}
- color -> Either a Color instance
new Color("red")
, a string"red"
, a hex value#FF0000
or a RGBA array[255, 0, 0, 1]
- setupCB -> Custom callback called on the object's initialization
(this, parent?)=>{}
s - setupResults -> The value returned by the
setupCB
call. - loopCB -> Custom callback called each frame for the object (obj)=>
- anchorPos -> The reference point from which the object's pos will be set. Can either be a pos
[x,y]
, another canvas object instance, or a callback(obj, Canvas or parent)=>{... return [x,y]}
(Defaults to the parent's pos, or[0, 0]
if the object has no parent). If your anchorPos references another object, make sure it is defined and initialized when used as the anchorPos value. - alwaysActive -> Whether the object stays active when outside the canvas bounds.
- initialized -> Whether the object has been initialized.
- parent -> The parent of the object. (Shape, Canvas, ...)
- rotation -> The object's rotation in degrees. Use the
rotateAt
,rotateBy
androtateTo
functions to modify. - scale -> The shape's X and Y scale factors
[scaleX, scaleY]
. Use thescaleAt
,scaleBy
andscaleTo
functions to modify. - visualEffects -> The visual effects of the object in an array:
[filter, compositeOperation, opacity]
.filter
is a string containing a regular css filter ("blur(5px)"
,url(#svgFilterId)
, etc).compositeOperation
is one ofRender.COMPOSITE_OPERATIONS
(see global composite operations for more information).opacity
is the alpha value of the object (in addition to the object's color alpha).
This class also defines other useful base functions, such as:
- Movements functions (
moveBy
,addForce
,follow
, ...) - Informative functions (
isWithin
,getInitPos
, ...) - Access to the object's animation play (
playAnim
)
The follow function: use follow()
to make an object follow a custom path:
- follow(duration, easing, action, progressSeparations)
/**
* Used to make an object follow a custom path
* @param {Number} duration: duration of the animation in ms
* @param {Function} easing: easing function
* @param {Function?} action: a custom callback that can be called in addition to the movement //newProg is 'prog' - the progress delimiter of the range
* @param {[[Number, Function], ...]} progressSeparations: list of callback paired with a progress range, the callback must return a position (prog, newProg, initX, initY)=>return [x,y]
* progressSeparations example: [0:(prog)=>[x1, y1]], [0.5:(prog, newProg)=>[x2, y2]] -> from 0% to 49% the pos from 1st callback is applied, from 50%-100% the pos from 2nd callback is applied
*/
// Example use, for 3 seconds, easeOutQuad, no custom callback, will travel in a sideways 'L' shape
let dx=400, dy=200
dot.follow(3000, Anim.easeOutQuad, null, [[0,(prog)=>[dx*prog, 0]], [0.5,(prog, newProg)=>[dx*0.5, dy*newProg]]])
Dot
The dot class is meant to be the core of most effects. It appears as a circular dot on the canvas by default.
The Dot constructor takes the following parameters:
- new Dot(pos, radius, color, setupCB, anchorPos, alwaysActive, disablePathCaching)
- pos, radius, color, setupCB, anchorPos, alwaysActive -> See the _Obj class.
Its other attribute is:
- connections -> a list referencing other dots, primarily to draw a connection between them.
- cachedPath -> The cached Path2D object or
null
if path caching is disabled (Controlled via thedisablePathCaching
constructor parameter or by thedisablePathCaching()
function)
To add or remove connections, use the following functions:
// Adding a connection with another dot
dot.addConnection(otherDot)
// Removing a connection
dot.removeConnection(otherDot)
To control whether a dot caches its path use the following functions:
// By default, path caching for dots is enabled.
// But for very dynamic dots (changes every frame), sometimes it might be better to disable caching.
// To disable path caching:
const dynamicDot = new Dot(null, null, null, null, null, null, true) // disables the path caching via constructor
existingDynamicDot.disablePathCaching() // disables the path caching via this function if the dot already exists
// To enable path caching back use:
existingDynamicDot.updateCachedPath()
To delete a dot, use the following function:
// Removes the dot completely
dot.remove()
Example use 1:
- A dot on its own (rare)
// Creating a lonely dot
const aloneDot = new Dot(
[0,0], // positioned at [0,0]
25, // 25px radius
[0,0,255,0.5], // blue at 50% opacity
()=>{ // setupCB, custom callback ran on dot's initialization
console.log("I am now added to the canvas and ready to go!")
}
)
// Add the dot as a standalone object (definition)
CVS.add(aloneDot)
Example use 2:
- Dots as part of a shape
// Creating a Shape containing some dots!
const squareLikeFormation = new Shape([0,0], [
new Dot([100, 100]), // Dots contained in a shape will take on some properties (color, radius, limit, ...) of the shape.
new Dot([150, 100]),
new Dot([150, 150]),
new Dot([100, 150])
], _Obj.DEFAULT_RADIUS, Color.DEFAULT_COLOR, Shape.DEFAULT_LIMIT)
// Add the shape along with all of its dots as a single unit. (reference)
CVS.add(squareLikeFormation)
Shape
The Shape class (or its inheritors) plays a crucial part in creating proper effects. It provides the needed control over a group of dots and is used to make pretty much any effect. An empty shape (containing no dots) on its own is not visible by default.
One of the main features is the drawEffectCB. This callback allows the creation of custom effects for each dot in the shape.
Effects are often ratio-based, meaning the intensity of the effect is based on the distance between the dot and the ratioPos. You can control the affected distance with the limit parameter, and the the object to which the distance\ratio is calculated with the ratioPosCB parameter.
The Shape constructor takes the following parameters:
- new Shape(pos, dots, radius, color, limit, drawEffectCB, ratioPosCB, setupCB, loopCB, anchorPos, alwaysActive, fragile)
- pos, radius, color, setupCB, loopCB, anchorPos, alwaysActive -> See the _Obj class.
- initDots -> Initial dots declaration. Can either be: an array of dots
[new Dot(...), existingDot, ...]
, a String (this will automatically call the shape's createFromString() function), or a callback(Shape, Canvas)=>{... return anArrayOfDots}
- dots -> Array of all the current dots contained by the shape.
- limit -> Defines the circular radius in which the dots' ratio is calculated. Each dot will have itself as its center to calculate the distance between it and the shape's ratioPos. (At the edges the ratio will be 0 and gradually gravitates to 1 at the center)
- drawEffectCB -> A custom callback containing the effects to display. It is run by every dot of the shape, every frame.
(render, dot, ratio, parentSetupResults, mouse, distance, parent, isActive, rawRatio)=>{...}
. - ratioPosCB? -> References the mouse position by default. Can be used to set a custom ratioPos target
(Shape, dots)=>{... return [x, y]}
. Can be disabled if set tonull
. - fragile? -> Whether the shape resets on document visibility change events. (Rarer, some continuous effects can break when the page is in the background due to the unusual deltaTime values sometimes occurring when the document is offscreen/unfocused)
To add one or many dots, use the add() function:
- add(dots)
// Creating and adding a new empty Shape to the canvas
const dummyShape = new Shape([100,100])
CVS.add(dummyShape)
// Later, adding a dot to the shape, at [150, 150] on the canvas
dummyShape.add(new Dot(50, 50))
// or many dots
dummyShape.add([new Dot(50, 50), new Dot(50, 100)])
Note, adding dots to a shape:
- Sets the dots' the dots' color to the one of shape, if not previously defined.
- Sets the dots' radius to the one of the shape, if not previously defined.
- Sets the dots' anchorPos to the shape's pos, if not previously defined.
- Sets the dots' alwaysActive property to that of the shape, if not previously defined.
- Sets the dots' parent attribute to reference the shape.
To modify dots' properties all at once, use the following functions:
- setRadius(radius, onlyReplaceDefaults), setColor(color, onlyReplaceDefaults), setLimit(limit, onlyReplaceDefaults), [enable/disable]DotsPathCaching()
// Sets the radius of all dummyShape's dots to 10
dummyShape.setRadius(10)
// Sets the color of all dummyShape's dots to red
dummyShape.setColor("red")
// Sets the limit of all dummyShape's dots to 100
dummyShape.setLimit(100)
// Disables the path caching of all dummyShape's dots
dummyShape.disableDotsPathCaching()
// Enables the path caching of all dummyShape's dots
dummyShape.enableDotsPathCaching()
To dynamically generate a formation of dots use the generate
functions:
- generate(yTrajectory, startOffset, length, gapX, yModifier, generationCallback)
// Generating a sine wave-based formation
const generatedDots = Shape.generate(
x=>Math.sin(x/50)*100, // make the y follow a sine wave pattern
[-50, 0], // the generation start is offset by -50 horizontally
1000, // the generation will span 1000 px in length
10, // the generation is sectioned in 10px intervals
[5, -5], // a range allowing random Y between the [min, max]
(dot, nextDot)=>{
dot.addConnection(nextDot) // adding a connection between each dot
}
)
// Creating a new shape containing the generated dots
const dummyShape = new Shape([0, 0], generatedDots)
// Adding the shape to the canvas
CVS.add(dummyShape)
To rotate or scale, use the multiple prebuilt rotate/scale functions:
- rotateBy(deg, centerPos?), - rotateAt(deg, centerPos?), - rotateTo(deg, time, easing?, force?, centerPos?)
// This will rotate all of dummyShape's dots around the shape's pos by 45 degrees
dummyShape.rotateBy(45)
// This will rotate all of dummyShape's dots around the pos [100, 100] at 180 degrees
dummyShape.rotateAt(180, [100, 100])
// This will smoothly rotate all of dummyShape's dots around the shape's pos at 125 degrees, over 5 seconds
dummyShape.rotateTo(125, 5000)
- scaleBy(scale, centerPos?), - scaleAt(scale, centerPos?), - scaleTo(scale, time, easing?, force?, centerPos?)
// This will scale the distance between all of dummyShape's dots by 2x horizontally, from the point [250, 100]
dummyShape.scaleBy([2, 1], [250, 100])
// This will scale the distance between all of dummyShape's dots to 4x, from the shape's pos
dummyShape.scaleAt([4, 4])
// This will smoothly scale the distance between all of dummyShape's dots to 2.5x horizontally and
// 3.5x vertically, from the shape's pos, over 3 seconds
dummyShape.scaleTo([2.5, 3.5], 3000)
To remove a dot or the entire shape, use the following functions:
- remove(), removeDot(dotOrId)
// This will remove the dot with the id 10
dummyShape.removeDot(10)
// This will remove the first dot of the shape
dummyShape.removeDot(dummyShape.dots[0])
// This will remove the entire shape, along with its dots!
dummyShape.remove()
To duplicate a shape, use the duplicate() function:
- duplicate()
// Creating a dummy shape
const dummyShape = new Shape([10,10], new Dot())
// Adding it to the canvas and thus initializing it
CVS.add(dummyShape)
// Creating a copy of the dummyShape (which needs to be initialized)
const dummyShapeCopy = dummyShape.duplicate()
// Adding it to the canvas
CVS.add(dummyShapeCopy)
// Moving the copy 100px to the right
dummyShapeCopy.moveBy([100, 0])
Example use 1:
- Simple shape with small mouse effects
// Creating a simple square-like shape
const simpleShape = new Shape([100,100],[
new Dot([-50, -50]),
new Dot([-50, 0]),
new Dot([-50, 50]),
new Dot([0, -50]),
new Dot([0, 50]),
new Dot([50, -50]),
new Dot([50, 0]),
new Dot([50, 50]),
], null, normalColorTester, 100, (render, dot, ratio, setupResults, mouse)=>{
// Changes the opacity and color according to mouse distance
dot.a = CDEUtils.mod(1, ratio, 0.8)
dot.r = CDEUtils.mod(255, ratio, -255)
dot.g = CDEUtils.mod(255, ratio, -255)
// Changes the dot's radius, from 2 times the default radius with a range of 80% (10px..2px), according to mouse distance
dot.radius = CDEUtils.mod(_Obj.DEFAULT_RADIUS*2, ratio, _Obj.DEFAULT_RADIUS*2*0.8)
// Draws a ring around the dot, at 5 times the radius
CanvasUtils.drawOuterRing(dot, [255,255,255,0.2], 5)
})
// Adding it to the canvas
CVS.add(simpleShape)
Example use 2:
- Single throwable dot, with color and radius effects
const draggableDotShape = new Shape([0,0], new Dot([10,10]), null, null, null, (render, dot, ratio, setupResults, mouse, dist, shape)=>{
// Checking if the mouse is over the dot and clicked, and changing the color according to the state
const mouseOn = dot.isWithin(mouse.pos, true)
if (mouseOn && mouse.clicked) dot.color = [255, 0, 0, 1]
else if (mouseOn) dot.color = [0, 255, 0, 1]
else dot.color = [255, 255, 255, 1]
// Draws a ring around the dot, at 3 times the radius, only visible if the mouse is near
CanvasUtils.drawOuterRing(dot, [255,255,255,CDEUtils.mod(0.3, ratio)], 3)
// Making the dot drawable
const dragAnim = setupResults
dragAnim(shape.firstDot, mouse, dist, ratio)
}, null, (shape)=>{
// Accessing the dot
const dot = shape.firstDot
// Adding an infinite anim that changes the radius size back and forth
dot.playAnim(new Anim((progress, i)=>{
dot.radius = i%2?25*(1-prog):25*prog
}, -750, Anim.easeOutQuad))
// Getting the dragging animation callback
const dragAnim = CanvasUtils.getDraggableDotCB()
// Making it available in the drawEffectCB as setupResults
return dragAnim
})
// Adding it to the canvas
CVS.add(draggableDotShape)
Example use 3:
- Linking a shape pos to another one with anchorPos
// Assuming we have simpleShape from example use 1 available...
// Creating a shape with a dot moving back and forth every second
const backAndForthDotShape = new Shape([200,200], new Dot([0,0], null, null, (dot, shape)=>{
let distance = 150, ix = dot.x
dot.playAnim(new Anim((progress, playCount, deltaTime)=>{
dot.x = ix + ((playCount % 2) == 0 ? 1 : -1) * distance * progress
if (progress == 1) ix = dot.x
}, -1000, Anim.easeOutBack))
})
)
// Setting simpleShape's anchorPos to the dot of backAndForthDotShape. (Using a callback since the dot doesn't exist yet)
simpleShape.anchorPos = () => backAndForthDotShape.firstDot
// Adding the shape to the canvas
CVS.add(backAndForthDotShape)
Filled Shape
The FilledShape class is a derivative of the Shape class. It allows to fill the area delimited by the shape's dots.
The FilledShape constructor takes the following parameters:
- new FilledShape(fillColor, dynamicUpdates, pos, dots, radius, color, limit, drawEffectCB, ratioPosCB, setupCB, loopCB, anchorPos, alwaysActive, fragile)
- pos, dots, radius, color, limit, drawEffectCB, ratioPosCB, setupCB, loopCB, anchorPos, alwaysActive, fragile -> See the Shape class.
- fillColor -> Defines the color of the shape's filling. Either a color value, a Gradient instance, or a callback returning any of the previous two
(render, shape)=>{... return [r, g, b, a]}
. - dynamicUpdates -> Whether the shape's fill area checks for updates every frame
To update the fill area manually: use the updatePath()
function:
- updatePath()
// ... Assuming there is a dummyFilledShape existing somewhere
// Moving it somewhere else
dummyFilledShape.moveAt(250, 250)
// If the dynamicUpdates parameter isn't set to 'true', the fill area will ONLY update IF the updatePath() function is called
dummyFilledShape.updatePath()
Example use 1:
- Simple red square
// Creating a simple filledShape
const myFilledShape = new FilledShape(
"red", // color of the fill area
true, // Automatically updates the fill area positions
[100, 100], // shape pos
[
new Dot([-50, -50]), // Top left corner
new Dot([50, -50]), // Top right corner
new Dot([50, 50]), // Bottom right corner
new Dot([-50, 50]) // Bottom left corner
]
)
// Adding it to the canvas
CVS.add(myFilledShape)
Grid
The Grid class is a derivative of the Shape class. It allows the creation of dot-based symbols / text. To create your own set of symbols (source), see the Grid Assets section.
The Grid constructor takes the following parameters:
- new Grid(keys, gaps, spacing, source, pos, radius, color, limit, drawEffectCB, ratioPosCB, setupCB, loopCB, anchorPos, alwaysActive, fragile)
- pos, radius, color, limit, drawEffectCB, ratioPosCB, setupCB, loopCB, anchorPos, alwaysActive, fragile -> See the Shape class.
- keys -> A string containing the characters to create.
- gaps -> The
[x, y]
distances within the dots. - source -> The source containing the symbol's definitions. See the Grid Assets section.
- spacing -> The distance between each symbol. (Letter spacing)
Example use 1:
- Displaying all A-Z letters, with a nice effect when passing the mouse over the shape
// Creating a Grid
const coolAlphabet = new Grid(
"abcdefg\nhijklm\nnopqrs\ntuvwxyz", // the keys corresponding to the alphabet letters, with some line breaks
[5, 5], // equal gaps, this will make the alphabet look a bit square-ish
50, // 50px letter spacing
GridAssets.DEFAULT_SOURCE, // default source
[10,10], // the shape position (The text will start from this point, as its top-left corner)
2, // 2px dot radius
null, // color is left undefined, the shape will assign it the default value
null, // limit is left defined, default value assigned (100)
(render, dot, ratio)=>{ // This is the drawEffectCB, which gets call for every dot of the shape, every frame
// This will make a nice proximity effect when the mouse is close.
// The mod() function and the ratio allow us to modify the dot radius with
// a linear interpolation based on the distance between the ratioPos (the mouse) and the current dot.
dot.radius = CDEUtils.mod(_Obj.DEFAULT_RADIUS, ratio, _Obj.DEFAULT_RADIUS) // DEFAULT_RADIUS = 5
// By default, no connections are drawn between the dots of a grid.
// We can use the drawDotConnections() function to draw them easily.
CanvasUtils.drawDotConnections(dot, [255,0,0,1])
}
)
// The grid is done and dusted, adding it to the canvas
CVS.add(coolAlphabet)
Example use 2:
- Creating a grid, that gets distorted around the area of the mouse
// Creating a grid with symbols that distort themselves on mouse hover
const distortedGrid = new Grid(
"abc\n123\n%?&", // symbols used
[7, 7], // gaps of 7px between each dot
50, // spacing of 50px between symbols
null, // using the default source
[100, 100], // pos
2, // dot's radius
"aliceblue",// dot's color
50, // limit of 50px
(render, dot, ratio, filterId)=>{ // grid's drawEffectCB
const scaleValue = CDEUtils.mod(50, ratio), // the scale value adjusted by the distance of the mouse
hasFilter = scaleValue>>0, // whether the current dot is affected by the filter (IMPORTANT FOR PERFORMANCES)
filterValue = hasFilter ? "url(#"+filterId+")" : "none" // adjusting the filter value
// accessing the <feDisplacementMap> element of the filter and updating its scale attribute
Canvas.getSVGFilter(filterId)[1].setAttribute("scale", scaleValue)
// drawing the symbols and applying some simple style changes, as well as the (↓) distortion filter. /!\ Also setting the (↓) "forceBatching" parameter to whether the filter is active or not
CanvasUtils.drawDotConnections(dot, render.profile5.update([255,0,0,1], filterValue, null, 1, 3), null, null, null, !hasFilter)
// finishing with a simple opacity effect for the dots
dot.a = CDEUtils.mod(1, ratio)
}, null, ()=>{ // grid's setupCB
// filter id to be used for the filter url
const filterId = "myFilter"
// loading a simple custom distortion SVG filter
Canvas.loadSVGFilter(`<svg>
<filter id="turbulence">
<feTurbulence type="turbulence" baseFrequency="0.01 0.02" numOctaves="1" result="NOISE"></feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="NOISE" scale="25">
</feDisplacementMap>
</filter>
</svg>`, filterId)
return filterId
})
// Adding the grid to the canvas
CVS.add(distortedGrid)
Grid Assets
This class contains the source declarations used by the Grid class. A source is basically a set of symbols, just like a font is a set of letters / numbers. You can create your own symbols and sources using the Grid Assets class. Sources are simply mapping key (any character, "A", "0", "%", ...) to symbol (a custom formation of dots).
The 'D
' constant:
D
(for directions) is a prebuilt object providing the cardinal and intercardinal directions. These are used to connect the dots together and draw specific lines between them, to create a symbol.
Creating a custom source:
A source is an object that should contain:
- The width and height of all its symbols. (As of now, the width and height must be equal)
- The symbol's definitions. (The key should be in uppercase)
Ex: { width: 3, height: 3, A: [...], B: [...], 5: [...] }
Creating custom symbols:
A symbol has this structure: [...[index, directions]]
. It is composed of a main array, containing the sub-arrays.
- The main array defines the vertical layers, a new layer is created each time a horizontal index is lower than the last.
- The sub-arrays each define a dot and contain its horizontal index and the directions of its connections.
Example for creating the letter "A" in a 5x5 source:
The final result should be dots placed like this
Top-Left | 0 | 1 | 2 | 3 | 4 | Top-Right |
---|---|---|---|---|---|---|
0 | o | |||||
1 | o | o | ||||
2 | o | o | o | o | o | |
3 | o | o | ||||
4 | o | o | ||||
Bottom-Left | Bottom-Right |
The steps to create a symbol:
Creating the main array ->
[ ]
// The main array [ ]
Creating the 1st vertical layer (vertical index 0). Since the "A" symbol doesn't have any dot at (0, 0) and (1, 0), we need to place the first dot at the cords (2, 0). To do that, we only need to specify the horizontal index in the sub-array. ->
[2]
.// The main array [ [2] // A dot at (2, 0) ]
Adding connections. A dot is now placed at (2,0), but it still has no connection to any other dot. To add connections, we use the '
D
' constant. In this example, the needed connections are with the dots at (1, 1) and (3, 1). To achieve these, we add a bottom-left (bl) connection and a bottom-right (br) connection, as the second parameter of the sub-array ->[2, D.bl + D.br]
Note: always define connections from upper dots to lower dots, and from left to right, to avoid redundancy.
// The main array [ [2, D.bl + D.br] // A dot at (2, 0), with connections to any dot at (1, 1) and (3, 1) ]
Creating the 2nd vertical layer (vertical index 1). Since the first layer only had a single dot, it is now completed, with the correct placement and connections. Let's continue the example with the 2nd layer. This layer has dots only at (1, 1) and (1, 3), so we can already create the following sub-arrays -> [1,] and [3,]. Looking at the "A" graph above, the entire 3rd layer (vertical index 2) is filled with dots. Though, to make the letter "A", we need to only connect to the dots at (0, 2) and (4, 2). We achieve these two connections by updating our previous sub-arrays like this -> [1, D.bl] and [3, D.br]
Note: A new vertical layer is created when the sub-array horizontal index is smaller than the previous sub-array's. (When the horizontal index is negative, it forces the creation of a new vertical layer and starts it at the absolute value of the negative horizontal index) (You can also use
Infinity
to "skip" a layer without putting any dot)// The main array [ // Here, we are at the vertical layer (vertical index 0) [2, D.bl + D.br], // A dot at (2, 0), with connections to any dot at (1, 1) and (3, 1) // Here, a new vertical layer is created (vertical index 1), because the first sub-array's horizontal index ([1, D.bl] -> 1) is smaller than the previous sub-array's horizontal index ([2, D.bl + D.br] -> 2) [1, D.bl], [3, D.br] // A dot at (1, 1), with connections to any dot at (0, 2) and another dot at (1, 3), with connections to any dot at (4, 2) ]
Continue the process until the symbol is fully formed. In the end, you should have something like this:
// The main array [ // Here, we are at the vertical layer (vertical index 0) [2, D.bl + D.br], // A dot at (2, 0), with connections to any dot at (1, 1) and (3, 1) // Here, a new vertical layer is created (vertical index 1), because the first sub-array's horizontal index ([1, D.bl] -> 1) is smaller than the previous sub-array's horizontal index ([2, D.bl + D.br] -> 2) [1, D.bl], [3, D.br], // A dot at (1, 1), with connections to any dot at (0, 2) and another dot at (1, 3), with connections to any dot at (4, 2) // (vertical index 2) [0, D.r + D.b], [1, D.r], [2, D.r], [3, D.r], [4, D.b], // (vertical index 3) [0,D.b], [4,D.b], // (vertical index 4) [0], [4] // Dots at the bottom (0, 4) and (4, 4), connected by the dots at vertical layer 3, but not initiating any connections themselves ] // Uncommented [ [2,D.bl+D.br], [1,D.bl],[3,D.br], [0,D.r+D.b],[1,D.r],[2,D.r],[3,D.r],[4,D.b], [0,D.b],[4,D.b], [0],[4] ]
Particularities:
- Leaving a sub-array's horizontal index empty (ex:
[,D.br]
), will result in it taking in value the increment of the previous sub-array's horizontal index. Ex ->[2, D.br]
,[, D.bl]
(here[, D.bl]
is equal to[3, D.bl]
). - Leaving a sub-array's connections parameter empty (ex:
[2]
), will make it so the dot does not initiate any connection. - Leaving a sub-array completely empty (ex:
[]
) logically implies that a dot will be created at the next horizontal index and that it won't initiate connections. - Important: You cannot start a new vertical layer using a sub-array without a horizontal index (empty).
In the end, the example source should look like this:
{
width:5,
height:5,
A: [
[2,D.bl+D.br],
[1,D.bl],[3,D.br],
[0,D.r+D.b],[1,D.r],[2,D.r],[3,D.r],[4,D.b],
[0,D.b],[4,D.b],
[0],[4]
],
// Other symbols ...
}
TextDisplay
The TextDisplay class allows the drawing of text as a canvas object.
The TextDisplay constructor takes the following parameters:
- new TextDisplay(text, pos, color, textStyles, drawMethod, maxWidth, setupCB, loopCB, anchorPos, alwaysActive)
- pos, color, setupCB, loopCB, anchorPos, alwaysActive -> See the _Obj / _BaseObj class.
- text -> The text to be displayed. Either a
String
or a callback(parent, this)=>{... return "textToDisplay"}
. - textStyles -> The style profile to be used for styling the text. Either a
TextStyles
or a callback(render)=>{... return TextStyles}
. - drawMethod -> The draw method used when drawing the text, Either
"FILL"
or"STROKE"
. - maxWidth? -> The max width in pixels of the drawn text.
Its other attribute is:
- size -> The text's width and height in pixels
[width, height]
. Does not take into account scaling, use thetrueSize
getter for adjusted size.
Example use 1:
- Drawing a simple spinning text
const helloWorldText = new TextDisplay(
"Hello World!", // Displayed text
[200, 100], // positioned at [200, 100]
"lightblue", // colored light blue
(render)=>render.textProfile1.update("italic 24px monospace"), // using the textProfile1 styles, only over writing the font
null, // leaving drawMethod to the default value ("FILL")
null, // leaving maxWidth to the default value (undefined)
(textDisplay)=>{// setupCB
// adding a spin animation, repeating every 3 seconds
textDisplay.playAnim(new Anim(prog=>{
// Updating the horizontal scale to go from 1, to -1, back to 1
textDisplay.scale = [Math.sin(Math.PI*prog*2), 1]
},-3000))
})
// Adding the object to the canvas.
CVS.add(helloWorldText)
ImageDisplay
The ImageDisplay class allows the drawing of images, videos and live camera/screen feeds.
The ImageDisplay constructor takes the following parameters:
- new ImageDisplay(source, pos, size, errorCB, setupCB, loopCB, anchorPos, alwaysActive)
- pos, setupCB, loopCB, anchorPos, alwaysActive -> See the _Obj / _BaseObj class.
- source -> The source of the image. One of
ImageDisplay.SOURCE_TYPES
. - size -> The display size of the image
[width, height]
. (Resizes the image) - errorCB -> A callback called when the source produces an error
(errorType, e?)=>
.
Its other attributes are:
- sourceCroppingPositions -> The source cropping positions. Delimits a rectangle which indicates the source drawing area to draw from:
[ [startX, startY], [endX, endY] ]
. (Defaults to no cropping)
Example use 1:
- Drawing an image from the web
// Creating an ImageDisplay with a url pointing to an image
const myCoolImage = new ImageDisplay("https://static.wikia.nocookie.net/ftgd/images/9/97/ExtremeDemon.png/revision/latest?cb=20240801164829")
// Adding the object to the canvas.
CVS.add(myCoolImage)
Example use 2:
- Drawing an image from a file, resizing it, and cropping it
// Creating an ImageDisplay by loading a local file, and adjusting the sizes
const myCoolImage = new ImageDisplay(
"./img/logo.png", // local file located at [currentFolder]/img/logo.png
[0,0], // position of the top-left corner
[100, 100] // rezises the image to 100x100
)
// Cropping the source image to use only from [20, 20] to [150, 150]
myCoolImage.sourceCroppingPositions = [[20,20], [150, 150]]
// Adding the object to the canvas.
CVS.add(myCoolImage)
Example use 3:
- Drawing and playing a video
// Creating an ImageDisplay playing a video
const dummyVideo = new ImageDisplay(
"./img/vidTest.mp4", // local file located at [currentFolder]/img/vidTest.mp4
null, // using default pos ([0, 0])
null, // using natural size (default)
(video)=>{
// SetupCB, runs when the source is loaded
// Automatically starts the video once loaded
video.playVideo()
}
)
// Adding the object to the canvas.
CVS.add(dummyVideo)
Example use 4:
- Drawing live feeds from the camera and screen
// Creating an ImageDisplay displaying the camera (requires user permission)
const cameraFeed = new ImageDisplay(
ImageDisplay.loadCamera(), // get the camera, with default settings
[0,0], // draw at origin
ImageDisplay.RESOLUTIONS.SD // shrink it down to 640x480
)
// Creating an ImageDisplay displaying a screen (requires user actions and permission)
const screenFeed = new ImageDisplay(
ImageDisplay.loadCapture(), // get the screen capture, with default settings
[640,0], // draw next to the camera display
[1920/4, 1080/4] // resize it to Full HD divided by 4
)
// Adding both objects to the canvas.
CVS.add(cameraFeed)
CVS.add(screenFeed)
Note: Canvas image smoothing property is disabled by default to improve performances.
AudioDisplay
The AudioDisplay class allows the visualization of audio from song, videos, live microphone / computer audio, etc, in cutomizable forms.
The AudioDisplay constructor takes the following parameters:
- new AudioDisplay(source, pos, color, binCB, sampleCount, disableAudio, offsetPourcent, loadErrorCB, setupCB, loopCB, anchorPos, alwaysActive)
- pos, color, setupCB, loopCB, anchorPos, alwaysActive -> See the _Obj / _BaseObj class.
- source -> The source of the audio. One of
AudioDisplay.SOURCE_TYPES
. - binCB -> A custom callback called for each bin of the FFT data array. Used to draw the audio.
(render, bin, atPos, accumulator audioDisplay, i, sampleCount, rawBin)=>{... return? [ [newX, newY], newAccumulatorValue ]}
- sampleCount -> The count of bins to use / display. Ex: if sampleCount is "32" and the display style is
BARS
, 32 bars will be displayed. Note: (fftSize is calculated by selecting the nearest valid value based on twice the provided sampleCount). - disableAudio -> Whether this AudioDisplay actually output outputs sounds/audio. Note: (This value does not affect the visual display, only whether you can hear what is playing or not).
- offsetPourcent -> A number between 0 and 1 representing the offset pourcent in the order of the bins when calling binCB.
- errorCB -> A callback called when the source produces an error
(errorType, e?)=>
.
Its other attributes are the following audio context / analyser / modifier nodes:
(see Web Audio API for more information)
- audioCtx
- audioAnalyser
- gainNode
- biquadFilterNode
- convolverNode
- waveShaperNode
- dynamicsCompressorNode
- pannerNode
- delayNode
Note: ↑ The audio chain is also defined in the above order.
Example use 1:
- Displaying the waveform of a .mp3 file
// Creating an AudioDisplay playing and displaying a local file
const audioDisplay = new AudioDisplay(
"./audio/song.mp3", // the filepath of the .mp3 file
[100,100], // the pos of the display
"lime", // color of the display
AudioDisplay.BARS(),// the display type (here we use the generic bars/waveform display)
64, // the sample count, here 64 bars will be displayed (and the fftSize will be 128)
false, // not disabling the audio so we can hear the song.mp3 playing
0, // no offset
(type, e)=>console.log("Dang it, error! Type:", type, " | ", e) // onerror callback
)
// Adding the object to the canvas.
CVS.add(audioDisplay)
Example use 2:
- Displaying the microphone output as a circle
// Creating an AudioDisplay displaying the microphone
const micDisplay = new AudioDisplay(
AudioDisplay.loadMicrophone(), // loading the microphone
[100,100], // the pos of the display
"lime", // color of the display
(render, bin, pos, audioDisplay, accumulator, i)=>{// binCB
const maxRadius = 500/AudioDisplay.MAX_NORMALISED_DATA_VALUE, // defining a max radius of 500px
precision = 100 // used to skip over some bins (the lowest this is, the more precise the display will be, but the more performance-heavy it will be too!)
// optimizing with the precision variable (only drawing every <precision> time)
if (i%precision==0) {
// drawing the circles, the radius is based on the current bin value, and it's style is based on this audioDisplay object's styles
render.batchStroke(Render.getArc(pos, maxRadius*bin), audioDisplay._color, audioDisplay.visualEffects)
}
},
2000, // the sample count, here a maximum of 2000 bins would be displayed (and the fftSize will be 4096)
true, // disabling the audio to prevent echo
)
// Adding the object to the canvas.
CVS.add(micDisplay)
Example use 3:
- Displaying the screen's audio and applying some audio modifications
// Loading and displaying the screen audio. For this, the user needs to select a valid tab/window/screen.
const audioDisplay = new AudioDisplay(AudioDisplay.loadScreenAudio(), [100,100], "lime", AudioDisplay.BARS(), 64)
// Applying some audio modifiers:
// Setting the volume to 200%
audioDisplay.setVolume(2)
// Sets the audio filter to keep low frequencies and cut high ones
audioDisplay.setBiquadFilterType(AudioDisplay.BIQUAD_FILTER_TYPES.LOWPASS)
// delays the audio by 1 second
audioDisplay.setDelay(1)
// sets the distortion level to 10
audioDisplay.setDistortionCurve(10)
// sets the origin of the audio to seem like it's coming from 1 meter to the left
audioDisplay.setOriginPos(-1)
// since this sounds pretty bad, you could also reset all the modifiers with this:
//audioDisplay.resetAudioModifiers()
// Adding the object to the canvas.
CVS.add(audioDisplay)
Example use 4:
- Creating a reverb effect using the convolverNode
// Taking back the audioDisplay from example 1.
const audioDisplay = new AudioDisplay("./audio/song.mp3", [100,100], "lime", AudioDisplay.BARS(), 64)
// For this to work, you need an impulse response file
// This will load the IR and, by default, assign its buffer to the convolverNode then connect the latter. (You also can handle this yourself by specifying the second parameter: "readyCallback")
audioDisplay.loadImpulseResponse("./audio/IR.wav")
// To disable this effect, disconnect the convolverNode:
// audioDisplay.disconnectConvolver()
// Adding the object to the canvas.
CVS.add(audioDisplay)
Note: Due to the high customizability of the display, the getAutomaticPosition
function of Pattern/Gradient classes is not available, therefore the positions need to be entered manually.
Color
The Color class represents a color and provides multiple utility functions such as HSV control, RGBA control, color finding, color format conversions, and color normalization.
Note: Direct HSV / RGBA controls are not supported for gradients
The Color constructor takes the following parameters:
- new Color(color, isChannel)
- color -> The color value in any supported format. (Text:
"red"
, RGBA:[255, 255, 255 ,1]
, HEX:"#123abcff", Gradient:new Gradient( ... )
, Color: an instance of this class) - isChannel -> If
true
, the Color instance will be considered a color channel and will not duplicate. (Channels are for global color distribution)
To convert a color to another format, use the convertTo() function:
- converTo(format=Color.FORMATS.RGBA, color)
const red = "red"
console.log("Here is red in some other formats:",
Color.convertTo(Color.FORMATS.RGBA, red)
Color.convertTo(Color.FORMATS.HSV, red)
Color.convertTo(Color.FORMATS.HEX, red)
)
Note: ConvertTo
doesn't support gradients.
To find the first position of a color in the canvas, use the findFirstPos() function:
- findFirstPos(canvas, color, useAlpha=false, temperance=Color.DEFAULT_TEMPERANCE, searchStart=Color.DEFAULT_SEARCH_START, areaSize=[])
const redColor = new Color([255, 0, 0, 1])
const whereRedIsAt = Color.findFirstPos(
CVS, // Canvas instance
redColor // Color instance
)
if (whereRedIsAt) console.log("The color red was found at: X:"+whereRedIsAt[0]+", Y:"+whereRedIsAt[1])
else console.log("The canvas doesn't contain the color red")
Gradient
The Gradient class allows the creation of custom linear / radial gradients. A Gradient instance can be used in the color and fillColor fields of canvas objects.
The Gradient constructor takes the following parameters:
- new Gradient(render, positions, colorStops, type, rotation)
- render -> The canvas Render instance, or context.
- positions -> The positions of the gradient. Providing a canvas object will automatically position it to cover the minimal area containing all of the provided objects. For manual positions: linear gradients:
[ [x1, y1], [x2, y2] ]
, radial gradients[ [x1, y1, r1], [x2, y2, r2] ]
, conic gradients:[ x, y ]
. - colorStops -> An array containing the difference colors and their range
[0..1, color]
. Ex:[ [0, "purple"], [0.5, [255,0,0,1]], [1, "#ABC123"] ]
. - type -> The type of gradient. Either: Linear, Radial or Conic. (Defaults to Linear)
- rotation -> The rotation in degrees of the gradient. (Not applicable for Radial gradients)
To manually update a Gradient, use the update() function:
- update()
// Creating a gradient
const customGradient = new Gradient(
CVS.ctx, // canvas context
[ [0, 0], [100, 100] ], // setting manual position
[[0, "red"], [1, "blue"]], // goes from red to blue
Gradient.TYPES.LINEAR // linear gradient
)
// Assigning the gradient to a dummy filled shape
dummyFilledShape.fillColor = customGradient
// Ex: Updating color stops
customGradient.colorStops = [[0, "green"], [1, "pink"]]
// Access the gradient assigned to the shape's filling and update it
dummyFilledShape.fillColorRaw.update()
Note: when using a Shape, a Dot, or a TextDisplay instance as the 'positions' parameter, the gradient will update every frame automatically.
Example use 1:
- Coloring a FilledShape with a gradient and making a rotating gradient effect
const gradientShape = new FilledShape(
// Creating and returning a linear gradient with a callback.
// This linear gradient will auto-position itself according to the shape's dots, start at 90deg rotation, and will go from purple->red->yellow-ish
(render, shape)=>new Gradient(render, shape, [[0, "purple"], [0.5, [255,0,0,1]], [1, "#ABC123"]], null, 90),
// Other parameters are used by the FilledShape, to make a square at [100, 100]
false,
[200, 200],
[
new Dot([-50, -50]), // Top left corner
new Dot([50, -50]), // Top right corner
new Dot([50, 50]), // Bottom right corner
new Dot([-50, 50]) // Bottom left corner
]
)
// Creating and playing the rotating gradient animation
gradientShape.playAnim(
new Anim((progress)=>{
// Getting the gradient instance
const gradient = gradientShape.fillColorRaw
// Rotating it
gradient.rotation = 360 * progress
}, -750) // repeating every 750 ms
)
// Adding it to the canvas
CVS.add(gradientShape)
Pattern
The Pattern class allows the creation image/video based colors. A Pattern instance can be used in the color and fillColor fields of canvas objects.
The Pattern constructor takes the following parameters:
- new Pattern(render, source, positions, sourceCroppingPositions, keepAspectRatio, forcedUpdates, rotation, errorCB, readyCB, frameRate, repeatMode)
- render -> The canvas Render instance, or context.
- source -> The source declaration of the pattern. One of
ImageDisplay.SOURCE_TYPES
. - positions -> The positions of the pattern. (
[ [x1, y1], [x2, y2] ]
) Providing a canvas object will automatically position it to cover the minimal area containing all of the provided objects. - sourceCroppingPositions -> The source cropping positions. Delimits a rectangle which indicates the source drawing area to draw from:
[ [startX, startY], [endX, endY] ]
. (Defaults to no cropping) - keepAspectRatio -> Whether the displayed pattern keeps the same aspect ratio when resizing.
- forcedUpdates -> Whether/How the pattern updates are forced. One of
Pattern.FORCE_UPDATE_LEVELS
. - rotation -> The pattern's current rotation in degrees.
- errorCB -> A callback called when the source produces an error
(errorType, e?)=>
. - readyCB -> Similar to
setupCB
. A callback that gets called when the source has been initialized(pattern)=>
. Note: this attribute doesn't transfer when calling the duplicate() function to avoid StackOverflow errors. - frameRate -> The update frequency of the current source. (Controls the frequency of video/canvas sources updates, as well as the frequency of any other sources when a visible property gets updated: e.g when the rotation gets changed)
- repeatMode -> Whether the pattern repeats horizontally/vertically. One of
Pattern.REPETITION_MODES
.
To manually get the rectangular area containing all of a specific object, use the _DynamicColor.getAutomaticPositions() function:
- getAutomaticPositions(obj)
// Creating a dummy shape
const dummyShape = new Shape([0, 0], [new Dot([50,50]), new Dot([100, 0])])
// Adding the shape to the canvas first, because it needs to be initialized before looking at its pos
CVS.add(dummyShape)
// Getting the area (see example use 4 for real use of this function)
const area = _DynamicColor.getAutomaticPositions(dummyShape)
console.log("My Dummy Shape fits perfectly into this area! -> ", area)
Example use 1:
- Coloring some dots with a custom image
// Creating a dummy shape with two big dots, here parts of the image are going to be visible through the dots
const dummyShape = new Shape([100, 100], [new Dot([250,250]), new Dot([0,0])], 50, (render, shape)=>
new Pattern(
render, // the render instance
"./img/img2.jpg", // the source of the image, here it's the path to a local file
shape, // making the pattern fit the shape size
null, // no source cropping
true // preserving the default aspect ratio
)
)
// Adding the shape to the canvas
CVS.add(dummyShape)
Example use 2:
- Coloring some text with a camera feed
// Creating a dummy text display
const dummyText = new TextDisplay("Hey, this is just\n some random text in\n order to fill up space,\n have a nice day! :D", [250, 250], (render, text)=>
new Pattern(
render, // the render instance
Pattern.loadCamera(), // the source of the pattern, here it's we are requesting access to the live camera feed
text, // making the pattern fit the size of the text
null, // no source cropping
false, // resizing will most likely change the aspect ratio
Pattern.FORCE_UPDATE_LEVELS.RESPECT_FRAME_RATE // automatically resizes the pattern if the textDisplay size/pos changes
))
// Adding the text to the canvas
CVS.add(dummyText)
Example use 3:
- Sharing VS duplicating a pattern
// DUPLICATING PATTERN (Can be performance-heavy when using dynamic patterns such as video, camera, etc)
// Creating and adding a basic shape that contains 9 dots in a 3x3 grid disposition
const shape1 = new Shape([100,100],[new Dot([-50, -50]), new Dot([-50, 0]), new Dot([-50, 50]), new Dot([0, -50]), new Dot([0, 0]), new Dot([0, 50]), new Dot([50, -50]), new Dot([50, 0]), new Dot([50, 50])], 25)
CVS.add(shape1)
// Creating a pattern that will get duplicated for each dot of shape1 (set the "positions" (↓) parameter to the placeholder value)
const duplicatedPattern = new Pattern(CVS.render, Pattern.loadCamera(), Pattern.PLACEHOLDER, null, null, null, null, null,
(pattern)=>{// readyCB
// once the camera is loaded, set the shape1's color to the value of the pattern
shape1.setColor(pattern)
}
)
// SHARED PATTERN
// Creating and adding a basic shape that contains 9 dots in a 3x3 grid disposition
const shape2 = new Shape([300,100],[new Dot([-50, -50]), new Dot([-50, 0]), new Dot([-50, 50]), new Dot([0, -50]), new Dot([0, 0]), new Dot([0, 50]), new Dot([50, -50]), new Dot([50, 0]), new Dot([50, 50])], 25)
CVS.add(shape2)
// Creating a pattern that will get duplicated for each dot of shape1 (set the "positions" (↓) parameter to the area containing all the dots)
const sharedPattern = new Pattern(CVS.render, Pattern.loadCamera(), _DynamicColor.getAutomaticPositions(shape2), null, null, null, null, null,
(pattern)=>{// readyCB
// once the camera is loaded, set the shape2's color to the value of the pattern
shape2.setColor(pattern)
}
)
Example use 4:
- Using a pattern to color non-objects (In this case, lines)
// Creating a variable containing the color of the grid's symbol lines
let gridLineColor = [255,0,0,1]
// Creating a simple grid displaying a couple of letters
const grid = new Grid("abc\nDEF\nghi", [5, 5], 50, null, [50,50], 2, null, null,
(render, dot, ratio, res, m, dist, shape, isActive)=>{// drawEffectCB
// simple effect to change the dot's radius
dot.radius = CDEUtils.mod(_Obj.DEFAULT_RADIUS, ratio, _Obj.DEFAULT_RADIUS)
// drawing the grid's lines (connections) using the previous variable (↓). Note: the variable could also have been defined in the setupCB
CanvasUtils.drawDotConnections(dot, render.profile1.update(gridLineColor, null, null, null, 2, [0]))
}
)
// Adding the grid to the canvas. (This initializes it, which is needed to properly run getAutomaticPositions() on it)
CVS.add(grid)
// Assigning a pattern to the gridLineColor variable so it displays the camera as the color
gridLineColor = new Pattern(CVS.render, Pattern.loadCamera(), _DynamicColor.getAutomaticPositions(grid), null, null, null, null, null,
(pattern)=>{// readyCB
// (Optional): You can also set the dot's color to be this pattern.
// once the camera is loaded, we're also setting the grid's color to the value of the pattern
grid.setColor(pattern)
}
)
Render
Render is a class that centralizes most context operations. It provides functions to get lines and text, as well as functions to stroke / fill them. Most of the calls to this class are automated via other classes (such as Dot and FilledShape), except for the utility line getters which allow more customization. It also provides access to style profiles for lines and text (RenderStyles, TextDisplay). Finally, it is automatically instantiated by, and linked to, any Canvas instance and should not be instantiated manually.
Example use 1:
- Manually drawing a custom bezier curve
// Running in the drawEffectCB of a dummy shape...
{
...
// Drawing a bezier curve from [100, 100] to [100, 200], using the default control points, in red
render.stroke(Render.getBezierCurve([100,100], [100, 200]), [255, 0, 0, 1])
}
Example use 2:
- Manually drawing a custom filled quadratic curve
// Running in the drawEffectCB of a dummy shape...
{
...
// Drawing a bezier curve from [100, 400] to [400, 300], using the default control points with 0.5 spread
render.fill(Render.getQuadCurve(startPos, endPos, Render.getDefaultQuadraticControlPos([100, 400], [400, 300], 0.5)))
}
RenderStyles
The RenderStyles class allows the customization of renders via style profiles when drawing with the Render class. By default, the following profiles are created and accessible via any Render instance: defaultProfile
and profile1
, to profile5
. There is also a profiles
array to add more custom profiles.
The RenderStyles constructor takes the following parameters:
- new RenderStyles(render, color, filter, compositeOperation, opacity, lineWidth, lineDash, lineDashOffset, lineJoin, lineCap)
- render -> The canvas Render instance.
- color -> Either an RGBA array
[r, g, b, a]
or aColor
instance. - filter -> A string containing a filter in CSS formatting:
"blur(5px)"
,url(#svgFilterId)
, etc. (Usage of filters may cause some performance issues, and some SVG filters can be invasive) - compositeOperation -> The composite operation used. One of
Render.COMPOSITE_OPERATIONS
(see global composite operations for more information) (some composite operations can be invasive) - opacity -> The alpha value of the object ranging from 0 to 1. (This alpha is additive to the object's color alpha).
- lineWidth -> The width in px of the drawn line.
- lineDash -> Gaps length within the line
- lineDashOffset -> Offset in px of the start of the gaps (dashes).
- lineJoin -> Determines the shape of line joins. Either: miter, bevel or round
- lineCap -> Determines the shape of line ends. Either: butt, square or round
Note: See MDN Canvas API documentation for more information on line styling properties.
To create a new style profile, use the duplicate() function:
- duplicate(render?, color?, lineWidth?, lineDash?, lineDashOffset?, lineJoin?, lineCap?)
// Creating a new style profile from the RenderStyles' template profile
const myNewStyleProfile = RenderStyles.DEFAULT_PROFILE.duplicate(CVS.render)
// Adding a new style profile to the render's custom profile list
CVS.render.profiles.push(myNewStyleProfile) //The style profile is now accessible via render.profiles
// OR
// use the render instance function: createCustomStylesProfile()