Phlox in calcit-js
Pixi.js DSL in ClojureScript with hot code swapping, inspired by Virtual DOMs. Currently only a small subset of Pixi.js features is supported.
Previews http://r.tiye.me/Phlox-GL/phlox/ .
Usage
render! to add canvas to <body/>:
ns app.main $
:require $ [] phlox.core :refer
[] hslx render! create-list rect circle text container graphics >>
defn comp-demo (data)
rect
{}
:position $ [] 800 40
:size $ [] 60 34
:fill $ hslx 40 80 80
:on $ :pointertap $ fn (e d!) (d! :demo nil)
text $ {}
:text "|Demo"
:position $ [] 808 44
:style $ {}
:fill (hslx 120 80 20)
:font-size 18
:font-family "|Josefin Sans"
defatom *store nil
defn dispatch! (op op-data)
reset! *store (updater @*store op op-data))
defn main ()
render! (comp-demo data) dispatch! ({})
defn reload! ()
render! (comp-container @*store) dispatch! $ {} (:swap? true)
Notice that Phlox uses :pointertap instead of :click for touch screen support.
Global keyboard events
Phlox supports a naive global event system for listening to keyboard events from elements:
{}
:on-keyboard $ {}
:down $ fn (e dispatch!)
:press $ fn (e dispatch!)
:up $ fn (e dispatch!)
Cursor and states
>> for branching states:
phlox.core/>> state :a
update-states for handling states change, used in updater:
phlox.cursor/update-states store $ [] cursor op-data
Color
Notice that Pixi.js takes colors in hex numbers.
{} $ :color 0xaaaaff
For HSL:
phlox.core/hslx 200 80 80
For HSLuv, based on hsluv:
phlox.core/hsluvx 200 80 80
For HCL:
phlox.core/hclx 200 80 80
Text input
To interact with text input:
phlox.input/request-text! e $ {}
:placeholder "|text.."
:initial "|demo"
:textarea? false
:style $ {}
fn (result) (println "|got:" result)
Math
Complex number
phlox.complex contains several util functions to work with complex numbers like [x y].
add, adds two complex numbersminus, adds one to anothertimes, mutiply two complex numbersrebase, actualy "divide", renamed since naming collision.divide-by, divide by scalar numberx.rand-point, returns a random[x y], takes 1 or 2 arguments
Shapes
a list of shapes
Container
Add a container:
container $ {}
:position $ [] 1 1
:pivot $ [] 0 0
:rotation 0
:alpha 1
:on $ {}
:pointertap (fn ())
:on-keyboard $ {}
:down (fn ())
simple version:
container $ {}
:position $ [] 1 1
Group
Alias for group.
Rectangle
Draw a rectangle:
rect $ {}
:position $ [] 1 2
:size $ [] 1 1
:line-style $ {}
:width 2
:color 0x000001
:alpha 1
:fill 0x000001
:on $ {}
:pointertap (fn ())
:radius 1
:rotation 1
:pivot $ [] 1 2
:alpha 1
:on-keyboard $ {}
:down (fn ())
simple version:
rect $ {}
:position $ [] 1 2
:size $ [] 1 1
:line-style $ {}
:width 2
:color 0x000001
:alpha 1
:fill 0x000001
Circle
Draw a circle:
circle $ {}
:position $ [] 1 1
:radius 1
:line-style $ {}
:width 2
:color 0x000001
:alpha 1
:fill 0x000001
:on $ {}
:pointertap (fn ())
:alpha 1
:on-keyboard $ {}
:down (fn ())
simple version:
circle $ {}
:position $ [] 1 1
:radius 1
:line-style $ {}
:width 2
:color 0x000001
:alpha 1
:fill 0x000001
Text
Draw text:
text $ {}
:text "demo"
:position $ [] 1 1
:pivot $ [] 0 0
:rotation 0
:alpha 1
:style $ {}
:fill "|red"
:font-size 14
:font-family "|Hind"
:on-keyboard $ {}
:down (fn ())
:align :centeris handled via special logic, need deeper investigation.
simple version:
text $ {}
:text "demo"
:position $ [] 1 1
:style $ {}
:fill "|red"
:font-size 14
:font-family "|Hind"
Graphics
Draw graphics(use phlox.core/g for validations):
graphics $ {}
:ops $ []
g :move-to $ [] 1 1
g :line-to $ [] 2 2
g :line-style $ {}
g :begin-fill $ {} (:color "red")
g :end-fill nil
g :close-path nil
g :arc-to $ {} (:p1 $ [] 200 200) (:p2 $ [] 240 180) (:radius 90)
g :arc $ {} (:center $ [] 260 120) (:radius 40) (:angle $ [] 70 60) (:anticlockwise? false)
g :bezier-to $ {} (:p1 $ [] 400 500) (:p2 $ [] 300 200) (:to-p $ 600 300)
g :quadratic-to $ {} (:p1 $ [] 400 100) (:to-p $ [] 500 400)
:position $ [] 1 1
:pivot $ [] 1 2
:rotation 0
:alpha 1
:on $ {}
:pointertap (fn ())
:on-keyboard $ {}
:down (fn ())
simple version:
graphics $ {}
:ops $ []
g :move-to $ [] 1 1
g :line-to $ [] 2 2
g :line-style $ {}
g :begin-fill $ {} (:color "red")
g :end-fill nil
g :close-path nil
:position $ [] 1 1
arc
you may also use :radian instead of :rotation to declare angles:
g :arc $ {} (:center $ [] 260 120) (:radius 40) (:anticlockwise? false)
:radian $ [] 0.1 0.2
create-list
requires 3 arguments, with last one in a list of pairs:
create-list :container ({})
-> (range 20)
map $ fn (idx)
[] idx $ text
{}
:text $ str idx
:style $ {}
:position $ [] 0 0
Shape name is probably :container but you may also change to another one.
polyline
Draw a continous line with points:
polyline $ {}
:style $ {} (:width 4)
:color $ hslx 40 100 60
:alpha 1
:position $ [] 300 300
:points $ []
[] 1 1
[] 2 2
line-segments
Draw discrete lines with pairs of points:
line-segments $ {}
:style $ {} (:width 4)
:color $ hslx 40 100 60
:alpha 1
:position $ [] 300 300
:segments $ []
[]
[] 1 1
[] 2 2
[]
[] 3 4
[] 5 6
Mesh(Experimental)
currently support of very simple feature from https://codesandbox.io/s/o432o .
Draw with geometry and shader:
mesh $ {} (:scale 1)
:position $ [] 300 200
:geometry $ {}
:attributes $ []
{} (:id |aVertexPosition) (:size 2)
:buffer $ [] -100 -100 100 -100 100 100 -100 100
{} (:id |aUvs) (:size 2)
:buffer $ [] 0 0 1 0 1 1 0 1
:index $ [] 0 1 2 0 3 2
:shader $ {}
:vertex-source $ inline-file |demo.vert
:fragment-source $ inline-file |demo.frag
:uniforms $ js-object (:uSampler2 sample-texture)
:time $ :x state
:uniformstakes a plain JavaScript object.:vertex-sourceand:fragment-sourcetake strings of shader code.
Vetex Shader
A demo:
precision mediump float;
attribute vec2 aVertexPosition;
attribute vec2 aUvs;
uniform mat3 translationMatrix;
uniform mat3 projectionMatrix;
varying vec2 vUvs;
void main() {
vUvs = aUvs;
gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
// gl_Position = vec4(1,1,1,0);
}
Fragment Shader
A demo:
precision mediump float;
varying vec2 vUvs;
void main() {
gl_FragColor = vec4(0.1, 0.2, 0.4, 1.0);
}
image
Draw image with PIXI/Sprite:
image $ {}
:url |https://cdn.tiye.me/logo/quamolit.png
:position $ [] 100 20
:size $ [] 100 100
:on $ {} $ :pointertap $ fn (e d)
println |clicked
Components
with options and logics.
comp-arrow
phlox.comp.arrwo/comp-arrow for arrows:
comp-arrow (>> states :demo1)
{}
:from $ :from state
:to $ :to state
:width 2
:arm-length 8
:on-change $ fn (from to d!)
d! cursor $ assoc state :from from :to to
comp-button
phlox.comp.button/comp-button provides a clickable button:
comp-button $ {}
:text "|DEMO BUTTON"
:position $ [] 100 0
:align-right? false
:on $ {}
:pointertap $ fn (e d!) (js/console.log "|pointertap event" e d!)
comp-button $ {}
:text "|Blue"
:position $ [] 100 60
:color (hslx 0 80 70)
:fill (hslx 200 80 40)
comp-button $ {}
:text "|Quick pointertap"
:position $ [] 100 0
:on-pointertap $ fn (e d!) (js/console.log "|pointertap event" e d!)
comp-messages
phlox.comp.messages/comp-messages for rendering messages:
comp-messages $ {}
:messages (:messages state)
:position $ [] 16 (- js/window.innerWidth 16)
:color (hslx 0 0 50)
:fill (hslx 0 0 30)
:bottom? false
:on-pointertap (fn (message d!))
comp-slider-point
Also comp-slider-point is a minimal version for comp-slider, it does not accept :titles.
comp-slider
phlox.comp.slider/comp-slider provides a little slider bar of a number, changes on dragging:
comp-slider (>> states :c) $ {}
:value (:c state)
:unit 10
:min 1
:max 10
:round? true
:position $ [] 20 120
:fill (hslx 50 90 70)
:color (hslx 200 90 30)
:on-change $ fn (value d!) (d! cursor (assoc state :c value))
comp-spin-slider
comp-spin-slider support change value via touch and spin:
comp-spin-slider (>> states :c) $ {}
:value (:c state)
:unit 10
:min 1
:max 10
:position $ [] 20 120
; :fill (hslx 50 90 70)
; :color (hslx 200 90 30)
:border-color $ hslx 200 90 70
:border-width 4
:label "|Name of value"
:fraction 1
:on-change $ fn (value d!) (d! cursor (assoc state :c value))
:on-move $ fn (pos d!) (d! cursor (assoc state :pos pos))
comp-drag-point
phlox.comp.drag-point/comp-drag-point provides a point for dragging:
comp-drag-point (>> states :p3) $ {}
:position (:p3 state)
:unit 0.4
:title "|DEMO"
:radius 6
:fill (hslx 0 90 60)
:alpha 1
:color (hslx 0 0 50)
:hide-text? false
:on-change $ fn (position d!) (d! cursor (assoc state :p3 position))
comp-switch
phlox.comp.switch/comp-switch provides a switch button:
comp-switch $ {}
:value (:value state)
:position $ [] 100 20
:title "|Custom title"
:on-change $ fn (value d!) (d! cursor (assoc state :value value))
comp-tabs
phlox.comp.tabs/comp-tabs provides a switch button:
comp-tabs
[]
[] :a |A
[] :b |B
, :a
{}
:position $ [] 100 20
fn (tab d!) 4 println |tab tab
Filters
Based on https://filters.pixijs.download/main/docs/index.html .
A demo of adding shadows, first add package:
yarn add @pixi/filter-drop-shadow
then add into :filters field:
ns demo.demo
:require
|@pixi/filter-drop-shadow :refer $ DropShadowFilter
text $ {} (:text "\"Shadows")
:style $ {}
:filters $ []
[] DropShadowFilter $ {}
:color $ hslx 10 90 100
:distance 2
:rotation 30
:alpha 1
:quality 4
:blur 6
For a filter ft internally it's running new to create instances of filters:
new (nth ft 0) $ to-js-data (nth ft 1)
During updates, only last part ft are compared.