Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
46dbff9
cascade transforms by composing canvas
bigmistqke Apr 8, 2023
b3b5932
default `style.pointerEvents` === false
bigmistqke Apr 8, 2023
1086834
cache `mergeProps` of `Drag` and `Hover`
bigmistqke Apr 8, 2023
bd1851f
introduce `context.flags` with `shouldHitTest``
bigmistqke Apr 8, 2023
7c57584
memo return-type of `createControlledProps`
bigmistqke Apr 8, 2023
aa905d4
update `Smiley` to include an options-modal
bigmistqke Apr 8, 2023
e311ff4
all hitTests now set `propagation` to `false`
bigmistqke Apr 8, 2023
a8d04b7
`transformedCallback`: set/reset ctx.transform
bigmistqke Apr 8, 2023
b97b8c5
`context.registerInteractiveToken`
bigmistqke Apr 8, 2023
6a24475
hitTest only interactive tokens
bigmistqke Apr 8, 2023
c87983f
only hitTest interactive `Group` and `Shape2D`
bigmistqke Apr 8, 2023
e1e32ff
cache `props.style` in `renderPath`
bigmistqke Apr 10, 2023
ccf103b
throttle mouse event
bigmistqke Apr 22, 2023
837da5d
pass `token` to controllers
bigmistqke Apr 22, 2023
59a6878
createTransformedCallback and createBounds
bigmistqke Apr 22, 2023
201b2b9
Canvas: render and debug in two stages
bigmistqke Apr 22, 2023
74c1409
parenthood: add `.debug`
bigmistqke Apr 22, 2023
023e21a
init and externalize variables `mergeGetters`
bigmistqke Apr 22, 2023
07adf46
add bounds to `CanvasToken`-type
bigmistqke Apr 22, 2023
7953c34
add `.token` `Shape2DProps.controllers`-type
bigmistqke Apr 22, 2023
442df32
update defaultBoundsProps
bigmistqke Apr 22, 2023
94f02ee
update `Rectangles`-example
bigmistqke Apr 22, 2023
a421f80
update `Smileys`-example
bigmistqke Apr 22, 2023
d638cab
update `main.yml`
bigmistqke Apr 22, 2023
dbc7b20
add `token` to clickStyle-controller (fix)
bigmistqke Apr 22, 2023
e11b4b7
`mergeGetters`: fix types
bigmistqke Apr 22, 2023
4c31564
`createShape2D`: update createControlledProps
bigmistqke Apr 22, 2023
b929238
`createPath2D` make style-prop optional
bigmistqke Apr 22, 2023
3c82d90
`Line` make style-prop optional
bigmistqke Apr 22, 2023
37dbdde
`Group` remove unnecessary imports
bigmistqke Apr 22, 2023
37428c7
add `vite:build` to scripts
bigmistqke Apr 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CI

on:
push:
branches: [main]
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: deploy pages
uses: JamesIves/github-pages-deploy-action@v4.4.1
with:
branch: gh-pages
folder: dev/dist
56 changes: 52 additions & 4 deletions dev/pages/Smileys.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ const Smiley = (props: { counter: number }) => {
<Group transform={{ position: { x: 0, y: 35 } }}>
<Arc
transform={{ position: { x: 40, y: 0 } }}
style={{ radius: 10, fill: 'black', stroke: false }}
style={{
radius: 10,
fill: 'black',
stroke: false,
pointerEvents: true,
'&:hover': {
fill: 'red',
},
}}
/>
<Arc
transform={{ position: { x: 80, y: 0 } }}
Expand Down Expand Up @@ -87,17 +95,57 @@ const App: Component = () => {
})`

const clock = createClock()
clock.start()
clock.start(1000 / 30)

const [amount, setAmount] = createSignal(300)
const [shouldUseClock, setShouldUseClock] = createSignal(false)

return (
<>
<div
style={{
position: 'absolute',
background: 'white',
padding: '5px',

margin: '5px',
'font-size': '10pt',
display: 'flex',
gap: '5px',
'font-family': 'monospace',
'flex-direction': 'column',
}}
>
<div style={{ display: 'flex', gap: '5px' }}>
<label>amount:</label>
<input
type="number"
style={{
border: 'none',
width: '50px',
'font-family': 'monospace',
}}
value={amount()}
onInput={e => setAmount(+e.currentTarget.value)}
step={10}
/>
</div>
<div style={{ display: 'flex', gap: '5px' }}>
<label style={{ flex: 1 }}>clock:</label>
<input
type="checkbox"
checked={shouldUseClock()}
onInput={e => setShouldUseClock(e.currentTarget.checked)}
/>
</div>
</div>
<Canvas
clock={clock.clock()}
clock={shouldUseClock() ? clock.clock() : undefined}
style={{ width: '100%', height: '100%', fill }}
alpha
stats
>
<For each={new Array(500).fill('')}>
<For each={new Array(amount()).fill('')}>
{() => <Smiley counter={clock.clock()} />}
</For>
</Canvas>
Expand Down
38 changes: 19 additions & 19 deletions dev/pages/rectangles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,29 @@ const App: Component = () => {
draggable
>
<For
each={new Array(100).fill('').map(v => ({
position: {
x: Math.random() * (window.innerWidth + 200) - 100,
y: Math.random() * (window.innerHeight + 200) - 100,
each={new Array(600).fill('').map(v => ({
transform: {
position: {
x: Math.random() * (window.innerWidth + 200) - 100,
y: Math.random() * (window.innerHeight + 200) - 100,
},
skew: {
y: Math.random() * 90,
},
},
fill: {
r: Math.random() * 215,
g: Math.random() * 215,
b: Math.random() * 215,
style: {
fill: {
r: Math.random() * 215,
g: Math.random() * 215,
b: Math.random() * 215,
},
stroke: false,
dimensions: { width: 100, height: 100 },
composite: 'hard-light' as const,
},
skewY: Math.random() * 90,
}))}
>
{data => (
<Rectangle
{...data}
dimensions={{ width: 100, height: 100 }}
lineWidth={20}
stroke="transparent"
controllers={[Drag()]}
composite="hard-light"
/>
)}
{data => <Rectangle {...data} controllers={[Drag()]} />}
</For>
</Canvas>
</>
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
},
"scripts": {
"dev": "vite serve dev",
"dev:build": "vite build dev",
"build": "tsup",
"test": "concurrently pnpm:test:*",
"test:client": "vitest",
Expand Down
124 changes: 86 additions & 38 deletions src/components/Canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import {
untrack,
} from 'solid-js'
import { createStore } from 'solid-js/store'
import { InternalContext } from 'src/context/InternalContext'
import {
InternalContext,
InternalContextType,
} from 'src/context/InternalContext'
import { UserContext } from 'src/context/UserContext'

import { CanvasToken, parser } from 'src/parser'
import {
CanvasFlags,
CanvasMouseEvent,
CanvasMouseEventTypes,
Color,
Expand All @@ -32,6 +36,7 @@ import { createMouseEventHandler } from 'src/utils/createMouseEventHandler'
import forEachReversed from 'src/utils/forEachReversed'
import { resolveColor } from 'src/utils/resolveColor'
import withContext from 'src/utils/withContext'
import { should } from 'vitest'

/**
* All `solid-canvas`-components have to be inside a `Canvas`
Expand Down Expand Up @@ -107,41 +112,82 @@ export const Canvas: Component<{

const matrix = createMatrix(() => props)

const flags: Record<CanvasFlags, boolean> = {
shouldHitTest: true,
hasInteractiveTokens: false,
}

const [interactiveTokens, setInteractiveTokens] = createSignal<CanvasToken[]>(
[],
)

createEffect(() => {
if (interactiveTokens().length > 0) {
setFlag('hasInteractiveTokens', true)
} else {
setFlag('hasInteractiveTokens', false)
}
})

const registerInteractiveToken = (token: CanvasToken, add = true) => {
if (add) {
setInteractiveTokens(tokens => [...tokens, token])
} else {
if (interactiveTokens().includes(token)) {
setInteractiveTokens(tokens => tokens.filter(t => t !== token))
}
}
}

const setFlag = (key: CanvasFlags, value: boolean) => {
flags[key] = value
}

const context: InternalContextType = {
ctx,
setFlag: setFlag,
get flags() {
return flags
},
get debug() {
return !!props.debug
},
get matrix() {
return matrix()
},
registerInteractiveToken,
get interactiveTokens() {
return interactiveTokens()
},
addEventListener: (
type: CanvasMouseEvent['type'],
callback: (event: CanvasMouseEvent) => void,
) => {
setEventListeners(type, listeners => [...listeners, callback])
},
removeEventListener: (
type: CanvasMouseEvent['type'],
callback: (event: CanvasMouseEvent) => void,
) => {
setEventListeners(type, listeners => {
const index = listeners.indexOf(callback)
const result = [
...listeners.slice(0, index),
...listeners.slice(index + 1),
]
return result
})
},
}

const tokens = resolveTokens(
parser,
withContext(
() => props.children,
[
{
context: InternalContext,
value: {
ctx,
get debug() {
return !!props.debug
},
get matrix() {
return matrix()
},
addEventListener: (
type: CanvasMouseEvent['type'],
callback: (event: CanvasMouseEvent) => void,
) => {
setEventListeners(type, listeners => [...listeners, callback])
},
removeEventListener: (
type: CanvasMouseEvent['type'],
callback: (event: CanvasMouseEvent) => void,
) => {
setEventListeners(type, listeners => {
const index = listeners.indexOf(callback)
const result = [
...listeners.slice(0, index),
...listeners.slice(index + 1),
]
return result
})
},
},
value: context,
},
{
context: UserContext,
Expand Down Expand Up @@ -201,10 +247,12 @@ export const Canvas: Component<{
ctx.restore()

forEachReversed(tokens(), token => {
if (props.debug && 'debug' in token.data) token.data.debug(ctx)
if ('render' in token.data) token.data.render(ctx)
})

ctx.resetTransform()
forEachReversed(tokens(), token => {
if (props.debug && 'debug' in token.data) token.data.debug(ctx)
})
if (props.fill) {
ctx.save()
ctx.globalCompositeOperation = 'destination-over'
Expand Down Expand Up @@ -232,7 +280,7 @@ export const Canvas: Component<{
}
}

const scheduled = createScheduled(fn => throttle(fn, 1000 / 120))
const scheduled = createScheduled(fn => throttle(fn))

createEffect(() => {
if (!!props.clock || props.clock === 0) return
Expand Down Expand Up @@ -264,8 +312,8 @@ export const Canvas: Component<{

const mouseMoveHandler = createMouseEventHandler(
'onMouseMove',
tokens,
ctx,
interactiveTokens,
context,
eventListeners,
event => {
props.onMouseMove?.(event)
Expand All @@ -274,8 +322,8 @@ export const Canvas: Component<{

const mouseDownHandler = createMouseEventHandler(
'onMouseDown',
tokens,
ctx,
interactiveTokens,
context,
eventListeners,
event => {
if (props.draggable) {
Expand All @@ -293,8 +341,8 @@ export const Canvas: Component<{

const mouseUpHandler = createMouseEventHandler(
'onMouseUp',
tokens,
ctx,
interactiveTokens,
context,
eventListeners,
event => {
props.onMouseUp?.(event)
Expand Down
Loading