bratik
TypeScript icon, indicating that this package has built-in type declarations

0.5.7 • Public • Published

bratik

Tiny, p5.js-like, typed toolkit to cakewalk through 2d canvas context.
see Demo page.


Installation

by npm:

npm i bratik

and then

import { getcanvas, ... } from "bratik"

or if you don't use npm you can import module from unpkg:

import { getcanvas, ... } from "https://unpkg.com/bratik@latest/dist/bratik.min.js"

or by script tag in your html page (this will create the global variable bratik):

<script src="https://unpkg.com/bratik@latest/dist/iife/bratik.min.js"></script>

Usage

bratik exports:

import
{
  getcanvas, pxratio,
  shape, vertex, arc, curve,
  line, circle, ellipse, rect,
  mask, clip,
  font, settext, text,
  LINEAR, CONIC, RADIAL, gradient,
  fill, stroke, clear, bg,
  frame, loop, stop, looping,
  CLOSE, PI, TAU
}
from "bratik"

Canvas creation

it takes two optional args: width, height

getcanvas(100, 100)

// OR
const {
  canvas, // DOM element with width and height of window
  ctx,    // CanvasRenderingContext2D
  width,  // width of window
  height  // height of window
} = getcanvas()

Pixel ratio, fill, stroke, clear, bg

get/set, default: client device pixel ratio

// get
const pr = pxratio()
// set
pxratio(2)

provide null as a color to fill/stroke to remove it.

stroke(
  color: string | CanvasGradient | null,
  width?: number,
  lineCap?: string,
  lineLoin?: string
)

fill(color: string | CanvasGradient | null)

clear() cleans canvas, bg(color) fill canvas with provided color.


Gradient

gradient() takes type of gradient (LINEAR | CONIC | RADIAL) tag as a first parameter, then other parameters need depending on type. For the radial gradient you can pass only 3 parameters (x, y, r), 4 parameters (x, y, r1, r2) or 6 like shown below.

const sunset = 
  gradient(LINEAR, x1: number, y1: number, x2: number, y2: number) ||
  gradient(CONIC, a: number, x: number, y: number) ||
  gradient(RADIAL, x1: number, y1: number, r1: number, x2?: number, y2?: number, r2?: number)

returns an object

{
  image: CanvasGradient,
  // gradient itself which you can provide into fill,
  // stroke or bg functions, like fill(sunset.image)
  reset: (...options?: number[]) => void
  // reset method to redefine gradient with options provided,
  // as those passed in gradient function after the type tag
  // or just reset it without options
  add: (offset: number, color: string) => void
  // after gradient initiation by type and options
  // add colors by this method, offset should be from 0 to 1
}

Example

const sunrise = gradient(LINEAR, 0, 0, 0, height)
sunrise.add(0, "deepskyblue")
sunrise.add(0.75, "lightpink")
sunrise.add(1, "lightgoldenrodyellow")
bg(sunrise.image)

Looper

loop takes a callback to run every animation frame. Call stop to stop the loop.

let x = 20
const play = () => {
  clear()
  stroke(null)
  fill("red")
  circle(x++, height / 2, 10)
  if (frame === 180) stop()
}
loop(play)
stop()

Animate

animate function takes an object of options:

{
  dur: number, // in ms (default: 1000)
  ease: string, // ease tag (default: "linear"),
  loop: boolean, // default: false
  // and callbacks:
  onstart, ontick, onpause, onend
}

returns a dinamicly mutable on every animation frame object:

{
  dur: number,
  ease: string,
  started: false,
  paused: false,
  ended: false,
  frame: number, // frame number
  time: number, // timestamp
  t: number, // float number from 0 to 1 changing by ease function
  // callbacks
  onstart, ontick, onpause, onend
  // methods
  pause, play, on
}

Example of how to handle entire animation with ontick callback only:

const particle = { x: width / 2, y: height / 2 }
stroke(null)
animate({
  ontick: () => {
    const {t} = move, R = (1 - t) * 255, G = 0, B = t * 255
    fill(`rgb(${R},${G},${B})`)
    particle.y = height / 2 + Math.sin(t * TAU) * (height / 2 - 10)
    particle.x = width / 2 + Math.cos(t * TAU) * (width / 2 - 10)
    circle(particle.x, particle.y, 10)
  }
}).play()

Example of animation with .on() method combined with loop(): .on(target, props) takes a target object (or array of objects) with props to animate "from" and an object (or array of objects) with props to animate "to" (gsap-like)

const
  particle = { x: 10, y: 10 },
  move = animate({ dur: 900 })

stroke(null)
fill("black")
move.on(particle, { x: width - 10, y: height - 10 })
loop(() => {
  circle(particle.x, particle.y, 10)
  if (
    particle.x === width  - 10 &&
    particle.y === height - 10
  ) stop()
})

Shape, circle, ellipse, rect, line

first call shape to initiate it, then you may call vertex(x, y), arc(x1, y1, x2, y2, r) or curve(x1, y1, x2, y2, x3?, y3?), once shape is finished call it again, provide CLOSE tag as a parameter to close it if needed.

const
  radius = 50,
  sidesnum = 6,
  angle = TAU / sidesnum,
  offset = PI / 2,

  vx = (i) => width / 2 + Math.cos(angle * i + offset) * radius,
  vy = (i) => height / 2 + Math.sin(angle * i + offset) * radius

shape()
for (let i = 0; i < sidesnum; i++) {
  i % 2 === 0
    ? vertex(vx(i), vy(i))
    : arc(vx(i), vy(i), vx(i + 1), vy(i + 1), radius)
}
curve(width / 2 + 50, height / 2 + 75, width / 2, height / 2 + 50)
shape(CLOSE)

cirlce takes: x, y of center and radius. ellipse takes: x, y of center, rx, ry radii; and optionaly: rotation, 'from' and 'to' of an arc in radians, and boolean if direction is counterclockwise. rect takes: x, y, width, height and optionally radius if you want rounded corners. line takes: x1, y1, x2, y2.

const
  size = 20,
  cx = width / 2,
  cy = height / 2,

  vx = (x, len) => x + Math.cos(frame * 0.01) * len,
  vy = (y, len) => y + Math.sin(frame * 0.01) * len

stroke("black", 2)
fill("white")
loop(() => {
  clear()
  ellipse(cx, cy, size * 2, size * 1.333, Math.PI / 4)
  rect(cx - size, cy - size, size * 2, size * 2, 5)
  circle(cx, cy, size)
  line(cx, cy, vx(cx, size), vy(cy, size))
})

Mask

You need to define mask shape in between mask() and mask(CLOSE) calls, it could be any number of — shape (and vertex, arc, curve), circle, ellipse or rect — calls. Then draw anything you like to clip by mask, after you done just call clip()

const hex = [ "FF", "00" ], { round, random } = Math
getcanvas(200)
const sun = gradient(RADIAL, 100, 100, 100 * Math.SQRT2)
const getcolor = () => Array(4).fill(0).map((_,i)=>i?hex[round(random())]:"#").join("")

// define mask
mask()
rect(50, 0, 100, 200, 40)
rect(0, 50, 200, 100, 40)
mask(CLOSE)

// drawing
stroke(null)
for (let i = 0; i < 4; i++) {
  const x = i % 2 ? 100 : 0, y = i > 1 ? 100 : 0
  sun.reset()
  sun.add(0, getcolor())
  sun.add(1, getcolor())
  fill(sun.image)
  rect(x, y, 100, 100, 10)
}
// clipping
clip()

you will get something like this:

drawing

Text

font takes size (number or string, to set line height provide it after slash like this "16/20"), family, and options like "bold". settext takes CanvasTextAlign, optionally CanvasTextBaseline and width. text takes string of content, x and y of its pivot and optionally width.

stroke(null)
fill("blue")
font(21, "monospace", "italic")
settext("center", "middle")
text("Hello world!", width / 2, height / 2)

Package Sidebar

Install

npm i bratik

Weekly Downloads

5

Version

0.5.7

License

MIT

Unpacked Size

25.5 kB

Total Files

6

Last publish

Collaborators

  • foretoo