-
Notifications
You must be signed in to change notification settings - Fork 1
/
usage-Ctx-interactive.html
218 lines (181 loc) · 6.13 KB
/
usage-Ctx-interactive.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<html>
<head>
<meta charset="UTF-8">
<title>Component - canvas demo</title>
<script src="../dist/component.min.js"></script>
<!-- include an enhanced canvas 2dContext API add-on -->
<script src="../dist/ctx.min.js"></script>
<style>
.container, video, .shadow-canvas {
border: 1px solid #222;
margin: 1em;
width: 640px;
height: 480px;
user-select: none;
}
.tooltip {
user-select: none;
}
</style>
</head>
<body>
<canvas class="container"></canvas>
<div class="tooltip"></div>
<script>
// Enable the enhanced canvas 2dContext API (optional).
// The extended `ctx` enables:
// - HDPI aware size() method
// - new drawing methods
// - most methods are chainable
// - save to image, record to video
Component.Ctx = Ctx;
// define a new component
const Foo = new Component({ frame: 0, x: 280, y: 100 });
// a reference to Foo.ctx, so we can access it outside the components view
let context,
// objects used by our interactive canvas
myRect,
myMickey,
myStar;
// define a "view", that draws to the components canvas
Foo.view = (props, ctx) => {
const frame = props.frame;
if (frame === 0) {
// set the canvas size (w, h, aspectRatio)
ctx.size(640, 480);
// keep references for outside the Foo component
context = ctx;
// Create interactive objects, using `ctx.create`
//myStar = ctx.create.star(100, 100, 50, 5, 0);
myStar = ctx.create.star({ x: 200, y: 400, radius: 50, points: 5, angle: 0 });
myRect = ctx.create.rect({ x: 30, y: 30, w: 200, h: 200 });
// Lets define a new, _custom_ "interactive canvas object":
// ...all 3 circles will be seen as _one_ object
ctx.create('mickey', function(x, y, r) {
[ctx, ctx.shadowCtx ].forEach(c => {
c.beginPath();
c.fillCircle(x,y,r/2, 360, true); // head
c.fillCircle(x-r/2,y-r/2,r/3, 360, true); // ear 1
c.fillCircle(x+r/2,y-r/2,r/3, 360, true); // ear 2
c.closePath();
});
});
myMickey = ctx.create.mickey({ x: 100, y: 100, r: 80 });
// debug
console.log('myStar', myStar);
console.log('myRect', myRect);
console.log('myMickey', myMickey);
}
//
// Let's start drawing in a loop from here
//
ctx.clear() // using `clear(true)` will clear transforms too
// update a shape, but dont redraw it
myRect.update({ w: 160 });
// lets use `adjust`: adds the given values to its current properties
myRect.adjust({ x: 0.25, y: 0, w: 0, h: -0.15 });
// which is the same as:
myRect.adjust({ x: 0.25, h: -0.15 });
// now render, optionally pass in some styles if needed/desired
myRect.draw({
fillStyle: myRect.hover ? 'yellow' : 'lightblue',
strokeStyle: myRect.hover ? 'red' : 'blue',
lineWidth: 3,
});
// ...
myMickey.on('click', props => console.log('mickey CLICK!'));
myMickey.on('hover', props => console.log('mickey HOVER!'));
// any ctx style props can be added to a "canvas object"
myMickey.fillStyle = myMickey.hover ? 'lightgreen' : 'cyan';
if (myMickey.drag) {
// update the props
myMickey.update({ x: ctx.mouse.x, y: ctx.mouse.y });
// ...or set using the *named* props (only works if we defined the props as an object)
myMickey.x = ctx.mouse.x;
myMickey.y = ctx.mouse.y;
}
// you can also update props and styles using functions
if (myMickey.click) {
myMickey.r = r => (r < 100) ? r + 2 : r; // grow the radius up to 100
} else {
myMickey.r = r => (r > 80) ? r - 2 : r; // shrink radius down to 80
}
// careful, any styles passed into draw() override all others
myMickey.draw({
//fillStyle: myMickey.hover ? '#c0c' : '#333',
});
// ...
if (myStar.drag) {
// now update it
myStar.update({ x: ctx.mouse.x, y: ctx.mouse.y });
// or simply (same as above)
myStar.x = ctx.mouse.x;
myStar.y = ctx.mouse.y;
// ...optionally interpolate the values
//myStar.x += (ctx.mouse.x - myStar.x) * 0.1;
//myStar.y += (ctx.mouse.y - myStar.y) * 0.1;
// ..which is the same as
//myStar.x = ctx.lerp(myStar.x, ctx.mouse.x, 0.01);
//myStar.y = ctx.lerp(myStar.y, ctx.mouse.y, 0.01);
}
// or use event handlers
myStar.on('hover', d => {
console.log('myStar hover', d.shape);
});
myStar.on('click', d => {
console.log('myStar clicked', d.id);
});
myStar.on('release', d => {
console.log('myStar released', d.id);
});
myStar.on('drag', d => {
console.log('myStar dragging', d.props);
// note that using `myStar.drag` (as above) works better than `onDrag()`
//myStar.update({ x: ctx.mouse.x, y: ctx.mouse.y });
});
myStar.lineWidth = 5;
myStar.strokeStyle = myStar.hover ? 'crimson' : 'orange';
myStar.fillStyle = myStar.click ? 'lightgreen' : 'yellow';
myStar.draw();
if (frame > 1000) return;
// re-render in a loop
Foo.setState({
frame: frame + 1,
});
};
// render to a canvas, and attach its context (`ctx`) to our component
Foo.render('.container', '2d');
// ..after the first frame renders (takes about 16 milliseconds)
// we can access `ctx` outside this components view, as `context`
setTimeout(() => {
// update a toolip with object info on hover and click
const tooltip = document.querySelector('.tooltip');
context.canvas.addEventListener('mousemove', event => {
// clear tooltip
tooltip.innerHTML = ``;
// if we are hovering on an object in the canvas
if (context.hoverObj) {
tooltip.innerHTML = `
<br> Mouse x,y: ${event.offsetX}, ${event.offsetY}
<br> Object id: ${context.hoverObj.id}
<br>Object props: ${typeof context.hoverObj.props === 'object' ? JSON.stringify(context.hoverObj.props) : context.hoverObj.props}
<br>Object drag: ${!!context.hoverObj.drag}
`;
}
// you can also check `myShape.drag` here
}, false);
context.canvas.addEventListener('mousedown', event => {
if (context.hoverObj) tooltip.innerHTML += `<br>Clicked`;
}, false);
}, 20);
// move stuff around a bit in an animated loop
//const loop = setInterval(() => {
// if (Foo.state.frame < 500) {
// Foo.setState({
// frame: Foo.state.frame + 1,
// });
// }
//}, 1000 / 30);
</script>
</body>
</html>