├── images
├── 1.png
├── 2.png
├── 3.png
└── 4.png
├── LICENSE
├── README.md
├── src
└── tink.js
└── bin
├── tink.js
└── tink.js.map
/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kittykatattack/tink/HEAD/images/1.png
--------------------------------------------------------------------------------
/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kittykatattack/tink/HEAD/images/2.png
--------------------------------------------------------------------------------
/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kittykatattack/tink/HEAD/images/3.png
--------------------------------------------------------------------------------
/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kittykatattack/tink/HEAD/images/4.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Tink - Interactivity utilities for Pixi (v3.0.11)
2 | =======================================
3 |
4 | Tink is an easy-to-use set of interactivity tools for the Pixi
5 | rendering engine. You can use Tink to easily create:
6 |
7 | - Drag and drop objects.
8 | - Click-able, touch-able buttons with customizable actions.
9 | - A universal pointer object that works for both touch and and the
10 | mouse.
11 | - Interactive sprites that behave like buttons.
12 |
13 | (Important! This library targets Pixi v3.0.11, which is the most stable version of Pixi, and is the only version I can recommend using. This library will eventually be upgraded for Pixi v4 when the v4 branch matures.)
14 |
15 | [Setting up](#settingup)
16 | [A universal pointer](#auniversalpointer)
17 | [Pointer interaction with sprites](#pointerinteractionwithsprites)
18 | [Drag and drop sprites](#draganddropsprites)
19 | [Buttons](#buttons)
20 | [Making buttons](#makingbuttons)
21 | [Making an interactive sprite](#makinganinteractivesprite)
22 | [Keyboard control](#keyboardcontrol)
23 | [Setting Tink's optional scale](#scale)
24 |
25 | Let's find out how to use Tink.
26 |
27 |
28 | Setting up
29 | ----------
30 |
31 | First, link to the `tink.js` script in your HTML file.
32 | ```js
33 |
34 | ```
35 | Then create a new instance of Tink at the beginning of your JavaScript program.
36 | Supply it with a reference to your running PIXI instance and the `renderer.view` object (the HTML5 canvas).
37 | ```js
38 | let t = new Tink(PIXI, renderer.view);
39 | ```
40 | The variable `t` now represents your running Tink instance. Generally you should
41 | create a new Tink instance after all the resources have loaded.
42 |
43 | Next call Tink's `update` method inside your game loop to update all of Tink's
44 | interactive objects each frame. Here's a simple game loop that will do
45 | the trick:
46 | ```js
47 | function gameLoop(){
48 |
49 | //Start the loop
50 | requestAnimationFrame(gameLoop);
51 |
52 | //Update Tink
53 | t.update();
54 |
55 | //Optionally, you probably also want to render your root Pixi
56 | //container, the `stage` object, in this loop:
57 | //renderer.render(stage);
58 | }
59 | ```
60 | This is what you need to do to get started with the examples ahead.
61 |
62 |
63 | A universal pointer
64 | -------------------
65 |
66 | Tink lets you make a pointer object that automatically figures
67 | out whether the user is interacting with a mouse or with touch.
68 | Use Tink's `makePointer` method to create a pointer.
69 | ```js
70 | pointer = t.makePointer();
71 | ```
72 | Usually one pointer will be enough for most games or applications,
73 | but you can make as many as you need. (Does your game or application
74 | require complex multi-touch interaction with gestures? Then consider
75 | using an excellent HTML5 library called [hammer.js](http://hammerjs.github.io/getting-started/).
76 |
77 | The `pointer` object has three user-definable methods that you can
78 | program: `press`, `release`, and `tap`. `press` is triggered when the
79 | left mouse button is pressed down, or the user presses his or her finger
80 | to the device screen. `release` is triggered when the mouse button is
81 | released, or the user lifts his or her from the screen. `tap` is triggered
82 | if the left mouse button is clicked, or the user taps the screen.
83 |
84 | Here's an example of how you can define these methods on the `pointer`:
85 | ```js
86 | pointer.press = () => console.log("The pointer was pressed");
87 | pointer.release = () => console.log("The pointer was released");
88 | pointer.tap = () => console.log("The pointer was tapped");
89 | ```
90 | Also use the `tap` method to capture mouse clicks.
91 |
92 | The `pointer` also has `x` and `y` properties that tell you its position
93 | on the canvas (Pixi's `renderer.view`.)
94 | ```js
95 | pointer.x
96 | pointer.y
97 | ```
98 | It also has three Boolean properties that tell you pointer's current
99 | state: `isUp`, `isDown` and `tapped`.
100 | ```js
101 | pointer.isUp
102 | pointer.isDown
103 | pointer.tapped
104 | ```
105 |
106 | If you need to hide the pointer for some reason, use the Boolean `visible`
107 | property.
108 | ```js
109 | //Hide the pointer
110 | pointer.visible = false;
111 |
112 | //Make the pointer visible
113 | pointer.visible = true;
114 | ```
115 |
116 |
117 | Pointer interaction with sprites
118 | --------------------------------
119 |
120 | The `pointer` has a `hitTestSprite` method that you can use to find out
121 | if the pointer is touching a sprite.
122 | ```js
123 | pointer.hitTestSprite(anySprite);
124 | ```
125 | If the pointer is within the rectangular area of a sprite,
126 | `hitTestSprite` will return `true`.
127 |
128 | `hitTestSprite` will also work with circular sprites. Just add a property
129 | to a sprite called `circular` and set it to `true`.
130 | ```js
131 | anyCircularSprite.circular = true;
132 | ```
133 | This flags `hitTestSprite` to use a circular collision detection algorithm
134 | instead of the default rectangular one. If you want to display a hand icon
135 | while the pointer is over sprite the you can set the pointer's
136 | `cursor` property to `"pointer"`. Setting it `"auto"` when the pointer
137 | leaves the sprite's area will display the default arrow icon. Here's some
138 | sample code you could use inside your game loop to enable this
139 | feature.
140 | ```js
141 | if (pointer.hitTestSprite(anySprite)) {
142 |
143 | //Display a hand icon while the pointer is over the sprite
144 | pointer.cursor = "pointer";
145 | }
146 | else {
147 |
148 | //Display the default arrow icon when the
149 | //pointer moves outside the sprite's area
150 | pointer.cursor = "auto";
151 | }
152 | ```
153 | `pointer.cursor` just references the HTML5 canvas's element's
154 | `style.cursor` property to achieve this, using two standard values
155 | from the HTML5 spec: `"pointer"` and `"auto"`. You can assign any cursor style
156 | value that you like. (A web search for "HTML style.cursor" will turn up a complete
157 | list of possible values.) You can also set this manually if you want
158 | to through Pixi's `renderer.view` object. Here's how:
159 | ```js
160 | renderer.view.style.cursor = "cursorStyle";
161 | ```
162 | These cursor styles will only be visible on a mouse-based interface;
163 | on a touch interface they're ignored.
164 |
165 |
166 | Drag and drop sprites
167 | ---------------------
168 |
169 | You can add drag-and-drop functionality to a sprite with Tink's
170 | `makeDraggable` method. Just supply it with a single sprite, or a list of
171 | sprites, that you want to make draggable.
172 | ```js
173 | t.makeDraggable(cat, tiger, hedgehog);
174 | ```
175 | You can then use the mouse or touch to drag the sprites around the
176 | canvas.
177 |
178 | 
179 |
180 | When you select a draggable sprite, its stacking order changes so that it
181 | appears above the other sprites. The mouse's arrow icon also changes
182 | to a hand when its over a draggable sprite.
183 |
184 | Draggable sprites have a Boolean property called `draggable` that is
185 | set to `true`. To disable dragging, set `draggable` to `false`.
186 | ```js
187 | anySprite.draggable = false;
188 | ```
189 | Setting it back to `true` will enable dragging again.
190 |
191 | To completely remove a sprite (or list of sprites) from the drag-and-drop
192 | system, use the `makeUndraggable` method, like this:
193 | ```js
194 | t.makeUndraggable(cat, tiger, hedgehog);
195 | ```
196 | Drag-and-drop is a fundamental interactive feature that can be used as
197 | the basis for making puzzles games, matching games, or sophisticated
198 | user interfaces.
199 |
200 |
201 | Buttons
202 | -------
203 | Buttons are an important UI component that you'll definitely want to use
204 | in your games and applications. Tink has a useful button method that
205 | lets you quickly create them. Before I show you how to make buttons,
206 | lets first find out what buttons actually are, can how you can use them.
207 |
208 | ### What are buttons?
209 |
210 | You can think of buttons as "clickable/touchable sprites". The most important
211 | thing you need to know about buttons is that they have **states** and
212 | **actions**.
213 | *States* define what the button looks like, and actions define what it does.
214 | Most buttons have three states:
215 |
216 | - **Up**: When the pointer is not touching the button.
217 | - **Over**: When the pointer is over the button.
218 | - **Down**: When the pointer is pressing down on the button.
219 |
220 | 
221 |
222 | Touch-based interfaces need only two states: up and down.
223 |
224 | With the button object that you'll learn to make in the next section,
225 | you be able access these states through the button's `state` property, like this:
226 | ```js
227 | playButton.state
228 | ```
229 | The `state` property could have the string value `"up"`, `"over"`,
230 | or `"down"`, which you could use in your game logic.
231 |
232 | Buttons also have *actions*:
233 |
234 | - **Press**: When the pointer presses the button.
235 | - **Release**: When the pointer is released from the button.
236 | - **Over**: When the pointer moves into the button's area.
237 | - **Out**: When the pointer moves out of the button's area.
238 | - **Tap**: When the button has been tapped (or clicked.)
239 |
240 | You can define these actions as user-definable methods, like this:
241 | ```js
242 | playButton.press = () => console.log("pressed");
243 | playButton.release = () => console.log("released");
244 | playButton.over = () => console.log("over");
245 | playButton.out = () => console.log("out");
246 | playButton.tap = () => console.log("tapped");
247 | ```
248 | In the button object that we'll make ahead, you'll able to access the button's
249 | "pressed" and "released" actions in a string property, like this:
250 | ```js
251 | playButton.action
252 | ```
253 | Got it? Good! So how do we actually make buttons?
254 |
255 |
256 | ### Making buttons
257 |
258 | First, start with three images that define the three button states.
259 | You might call them "up.png", "over.png", and "down.png". Then add those three
260 | images to a tileset, or as frames in a texture atlas.
261 |
262 | 
263 |
264 | Although having three image states is standard, sometimes buttons have only two image states.
265 | This is particularly true of touch-only buttons, which don't have an "over" state. The
266 | button object that we're going to make ahead will use three images if they're available,
267 | but if it only has two, Tink will assign these to the "up" and "down" states.
268 |
269 | Next, publish the texture atlas and load it into your program:
270 | ```js
271 | PIXI.loader
272 | .add("images/button.json")
273 | .load(setup);
274 | ```
275 | Then in the `setup` function where you initialize your sprites, create an array that
276 | references each of the three button frames in this order: up, over and down.
277 | ```js
278 | function setup() {
279 |
280 | //Create an alias for the texture atlas frame ids
281 | let id = PIXI.loader.resources["images/button.json"].textures;
282 |
283 | let buttonFrames = [
284 | id["up.png"],
285 | id["over.png"],
286 | id["down.png"]
287 | ];
288 | }
289 | ```
290 | These don't have to be frame ids: you can use an array of any Pixi
291 | textures, like single image textures if you want to.
292 |
293 | Finally, use Tink's `button` method to create the button. Supply the
294 | `buttonFrames` array as the first argument.
295 | ```js
296 | let playButton = t.button(buttonFrames, 32, 96);
297 | ```
298 | The second and third optional arguments are the button's `x` and `y` position.
299 | And don't forget to add the button to the `stage` (Pixi's root
300 | container object)!
301 | ```js
302 | stage.addChild(playButton);
303 | ```
304 | You now have a useful button object that you can use in any game or
305 | application.
306 |
307 | 
308 |
309 | At its heart, a button is just an ordinary Pixi `MovieClip` with extra properties
310 | and methods, so you can treat it like any other `MovieClip`object.
311 |
312 |
313 | Making an interactive sprite
314 | ----------------------------
315 |
316 | Tink has another useful method called `makeInteractive` that lets you add button
317 | properties and methods to any ordinary sprite.
318 | ```js
319 | t.makeInteractive(anySprite);
320 | ```
321 | This lets you turn any sprite into a button-like object. You can now
322 | assign `press` or `release` methods to the sprite, and access its
323 | `state` and `action` properties, like this:
324 | ```js
325 | anySprite.press = () => {
326 | //Do something when the pointer presses the sprite
327 | };
328 |
329 | anySprite.release = () => {
330 | //Do something when the pointer is released after pressing the sprite
331 | };
332 | ```
333 | If you want any sprite to behave like a button, use `makeInteractive`!
334 |
335 |
336 | Keyboard control
337 | ----------------
338 |
339 | `keyboard` is a method that listens for and captures keyboard events. It's really
340 | just a convenient wrapper function for HTML `keyup` and `keydown` events
341 | so that you can keep your application code clutter-free and easier to write and read.
342 | Here's how to use the `keyboard` method. Create a new keyboard object like this:
343 | ```js
344 | let keyObject = t.keyboard(asciiKeyCodeNumber);
345 | ```
346 | Its one argument is the ASCII key code number of the keyboard key
347 | that you want to listen for. [Here's a list of ASCII key codes you can
348 | use](http://www.asciitable.com).
349 | Then assign `press` and `release` methods to the keyboard object like this:
350 | ```js
351 | keyObject.press = () => {
352 | //key object pressed
353 | };
354 | keyObject.release = () => {
355 | //key object released
356 | };
357 | ```
358 | Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to
359 | check the state of each key.
360 |
361 | Tink has another convenience method called `arrowControl` that lets you
362 | quickly create a 4 direction controller for sprites using the
363 | keyboard arrow keys. It's useful for quickly prototyping game ideas.
364 | Supply the `arrowControl` method with the sprite you want to control
365 | and the pixels per frame that you want it to move:
366 | ```js
367 | t.arrowControl(anySprite, 5);
368 | ```
369 | Then just update your sprite's velocity inside your game loop, like
370 | this:
371 | ```js
372 | function gameLoop() {
373 | requestAnimationFrame(gameLoop);
374 |
375 | anySprite.x += vx;
376 | anySprite.y += vy
377 | }
378 | ```
379 | You'll then be able to move the sprite in all four directions using
380 | the arrow keys.
381 | (For the `arrowControl` method to work, your sprite needs to have `vx` and `vy` properties
382 | that refer to the sprite's velocity.)
383 |
384 |
385 | Setting Tink's optional scale
386 | -----------------------------
387 |
388 | You now know everything you need to know about using Tink, so go ahead and start using it!
389 | But, if you want to re-size or re-scale Pixi's renderer inside the
390 | browser window, there's one more thing you need to know.
391 |
392 | You can use an optional helper function called `scaleToWindow` that
393 | will automatically scale your Pixi renderer to the maximum size of
394 | the browser window. Visit `scaleToWindow`'s [source code repository](https://github.com/kittykatattack/scaleToWindow) to
395 | find out how to use it. Tink's constructor has an optional second argument: `scale`. The
396 | default `scale` value is 1, but if you've re-scaled the canvas using
397 | the `scaleToWindow` function, supply `scaleToWindow`'s return value.
398 | Here's an example of what this might look like:
399 |
400 | First, run `scaleToWindow` and capture the returned `scale` value at the beginning of your program.
401 | ```js
402 | let scale = scaleToWindow(renderer.view);
403 | ```
404 | Next, create a new instance of Tink and supply the `scale` value as the second argument in the constructor.
405 | ```js
406 | let t = new Tink(PIXI, renderer.view, scale);
407 | ```
408 | This will ensure that the coordinates that Tink uses will match the canvas's scaled pixel coordinates.
409 |
410 | Finally, make sure to update the Tink scale if you also opted for rescaling the canvas element every time the size of the browser window has changed:
411 | ```js
412 | window.addEventListener("resize", function(event){
413 | let scale = scaleToWindow(anyCanvasElement);
414 | t.scale(scale)
415 | });
416 | ```
417 |
--------------------------------------------------------------------------------
/src/tink.js:
--------------------------------------------------------------------------------
1 | class Tink {
2 | constructor(PIXI, element, scale = 1) {
3 |
4 | //Add element and scale properties
5 | this.element = element;
6 | this._scale = scale;
7 |
8 | //An array to store all the draggable sprites
9 | this.draggableSprites = [];
10 |
11 | //An array to store all the pointer objects
12 | //(there will usually just be one)
13 | this.pointers = [];
14 |
15 | //An array to store all the buttons and button-like
16 | //interactive sprites
17 | this.buttons = [];
18 |
19 | //A local PIXI reference
20 | this.PIXI = PIXI;
21 |
22 | //Aliases for Pixi objects
23 | this.TextureCache = this.PIXI.utils.TextureCache;
24 |
25 | //Note: change MovieClip to AnimatedSprite for Pixi v4
26 | this.AnimatedSprite = this.PIXI.extras.MovieClip;
27 | this.Texture = this.PIXI.Texture;
28 | }
29 |
30 | get scale() {
31 | return this._scale;
32 | }
33 |
34 | set scale(value) {
35 | this._scale = value;
36 |
37 | //Update scale values for all pointers
38 | this.pointers.forEach(pointer => pointer.scale = value);
39 | }
40 |
41 | //`makeDraggable` lets you make a drag-and-drop sprite by pushing it
42 | //into the `draggableSprites` array
43 | makeDraggable(...sprites) {
44 |
45 | //If the first argument isn't an array of sprites...
46 | if (!(sprites[0] instanceof Array)) {
47 | sprites.forEach(sprite => {
48 | this.draggableSprites.push(sprite);
49 |
50 | //If the sprite's `draggable` property hasn't already been defined by
51 | //another library, like Hexi, define it
52 | if (sprite.draggable === undefined) {
53 | sprite.draggable = true;
54 | sprite._localDraggableAllocation = true;
55 | }
56 | });
57 | }
58 |
59 | //If the first argument is an array of sprites...
60 | else {
61 | let spritesArray = sprites[0];
62 | if (spritesArray.length > 0) {
63 | for (let i = spritesArray.length - 1; i >= 0; i--) {
64 | let sprite = spritesArray[i];
65 | this.draggableSprites.push(sprite);
66 |
67 | //If the sprite's `draggable` property hasn't already been defined by
68 | //another library, like Hexi, define it
69 | if (sprite.draggable === undefined) {
70 | sprite.draggable = true;
71 | sprite._localDraggableAllocation = true;
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
78 | //`makeUndraggable` removes the sprite from the `draggableSprites`
79 | //array
80 | makeUndraggable(...sprites) {
81 |
82 | //If the first argument isn't an array of sprites...
83 | if (!(sprites[0] instanceof Array)) {
84 | sprites.forEach(sprite => {
85 | this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);
86 | if (sprite._localDraggableAllocation === true) sprite.draggable = false;
87 | });
88 | }
89 |
90 | //If the first argument is an array of sprites
91 | else {
92 | let spritesArray = sprites[0];
93 | if (spritesArray.length > 0) {
94 | for (let i = spritesArray.length - 1; i >= 0; i--) {
95 | let sprite = spritesArray[i];
96 | this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);
97 | if (sprite._localDraggableAllocation === true) sprite.draggable = false;
98 | }
99 | }
100 | }
101 | }
102 |
103 | makePointer(element = this.element, scale = this.scale) {
104 |
105 | //Get a reference to Tink's global `draggableSprites` array
106 | let draggableSprites = this.draggableSprites;
107 |
108 | //Get a reference to Tink's `addGlobalPositionProperties` method
109 | let addGlobalPositionProperties = this.addGlobalPositionProperties;
110 |
111 | //The pointer object will be returned by this function
112 | let pointer = {
113 | element: element,
114 | _scale: scale,
115 |
116 | //Private x and y properties
117 | _x: 0,
118 | _y: 0,
119 |
120 | //Width and height
121 | width: 1,
122 | height: 1,
123 |
124 | //The public x and y properties are divided by the scale. If the
125 | //HTML element that the pointer is sensitive to (like the canvas)
126 | //is scaled up or down, you can change the `scale` value to
127 | //correct the pointer's position values
128 | get x() {
129 | return this._x / this.scale;
130 | },
131 | get y() {
132 | return this._y / this.scale;
133 | },
134 |
135 | //Add `centerX` and `centerY` getters so that we
136 | //can use the pointer's coordinates with easing
137 | //and collision functions
138 | get centerX() {
139 | return this.x;
140 | },
141 | get centerY() {
142 | return this.y;
143 | },
144 |
145 | //`position` returns an object with x and y properties that
146 | //contain the pointer's position
147 | get position() {
148 | return {
149 | x: this.x,
150 | y: this.y
151 | };
152 | },
153 |
154 | get scale() {
155 | return this._scale;
156 | },
157 | set scale(value) {
158 | this._scale = value;
159 | },
160 |
161 | //Add a `cursor` getter/setter to change the pointer's cursor
162 | //style. Values can be "pointer" (for a hand icon) or "auto" for
163 | //an ordinary arrow icon.
164 | get cursor() {
165 | return this.element.style.cursor;
166 | },
167 | set cursor(value) {
168 | this.element.style.cursor = value;
169 | },
170 |
171 | //Booleans to track the pointer state
172 | isDown: false,
173 | isUp: true,
174 | tapped: false,
175 |
176 | //Properties to help measure the time between up and down states
177 | downTime: 0,
178 | elapsedTime: 0,
179 |
180 | //Optional `press`,`release` and `tap` methods
181 | press: undefined,
182 | release: undefined,
183 | tap: undefined,
184 |
185 | //A `dragSprite` property to help with drag and drop
186 | dragSprite: null,
187 |
188 | //The drag offsets to help drag sprites
189 | dragOffsetX: 0,
190 | dragOffsetY: 0,
191 |
192 | //A property to check whether or not the pointer
193 | //is visible
194 | _visible: true,
195 | get visible() {
196 | return this._visible;
197 | },
198 | set visible(value) {
199 | if (value === true) {
200 | this.cursor = "auto";
201 | } else {
202 | this.cursor = "none";
203 | }
204 | this._visible = value;
205 | },
206 |
207 | //The pointer's mouse `moveHandler`
208 | moveHandler(event) {
209 |
210 | //Get the element that's firing the event
211 | let element = event.target;
212 |
213 | //Find the pointer’s x and y position (for mouse).
214 | //Subtract the element's top and left offset from the browser window
215 | this._x = (event.pageX - element.offsetLeft);
216 | this._y = (event.pageY - element.offsetTop);
217 |
218 | //Prevent the event's default behavior
219 | event.preventDefault();
220 | },
221 |
222 | //The pointer's `touchmoveHandler`
223 | touchmoveHandler(event) {
224 | let element = event.target;
225 |
226 | //Find the touch point's x and y position
227 | this._x = (event.targetTouches[0].pageX - element.offsetLeft);
228 | this._y = (event.targetTouches[0].pageY - element.offsetTop);
229 | event.preventDefault();
230 | },
231 |
232 | //The pointer's `downHandler`
233 | downHandler(event) {
234 |
235 | //Set the down states
236 | this.isDown = true;
237 | this.isUp = false;
238 | this.tapped = false;
239 |
240 | //Capture the current time
241 | this.downTime = Date.now();
242 |
243 | //Call the `press` method if it's been assigned
244 | if (this.press) this.press();
245 | event.preventDefault();
246 | },
247 |
248 | //The pointer's `touchstartHandler`
249 | touchstartHandler(event) {
250 | let element = event.target;
251 |
252 | //Find the touch point's x and y position
253 | this._x = event.targetTouches[0].pageX - element.offsetLeft;
254 | this._y = event.targetTouches[0].pageY - element.offsetTop;
255 |
256 | //Set the down states
257 | this.isDown = true;
258 | this.isUp = false;
259 | this.tapped = false;
260 |
261 | //Capture the current time
262 | this.downTime = Date.now();
263 |
264 | //Call the `press` method if it's been assigned
265 | if (this.press) this.press();
266 | event.preventDefault();
267 | },
268 |
269 | //The pointer's `upHandler`
270 | upHandler(event) {
271 |
272 | //Figure out how much time the pointer has been down
273 | this.elapsedTime = Math.abs(this.downTime - Date.now());
274 |
275 | //If it's less than 200 milliseconds, it must be a tap or click
276 | if (this.elapsedTime <= 200 && this.tapped === false) {
277 | this.tapped = true;
278 |
279 | //Call the `tap` method if it's been assigned
280 | if (this.tap) this.tap();
281 | }
282 | this.isUp = true;
283 | this.isDown = false;
284 |
285 | //Call the `release` method if it's been assigned
286 | if (this.release) this.release();
287 |
288 | //`event.preventDefault();` needs to be disabled to prevent range sliders
289 | //from getting trapped in Firefox (and possibly Safari)
290 | //event.preventDefault();
291 | },
292 |
293 | //The pointer's `touchendHandler`
294 | touchendHandler(event) {
295 |
296 | //Figure out how much time the pointer has been down
297 | this.elapsedTime = Math.abs(this.downTime - Date.now());
298 |
299 | //If it's less than 200 milliseconds, it must be a tap or click
300 | if (this.elapsedTime <= 200 && this.tapped === false) {
301 | this.tapped = true;
302 |
303 | //Call the `tap` method if it's been assigned
304 | if (this.tap) this.tap();
305 | }
306 | this.isUp = true;
307 | this.isDown = false;
308 |
309 | //Call the `release` method if it's been assigned
310 | if (this.release) this.release();
311 |
312 | //event.preventDefault();
313 | },
314 |
315 | //`hitTestSprite` figures out if the pointer is touching a sprite
316 | hitTestSprite(sprite) {
317 |
318 | //Add global `gx` and `gy` properties to the sprite if they
319 | //don't already exist
320 | addGlobalPositionProperties(sprite);
321 |
322 | //The `hit` variable will become `true` if the pointer is
323 | //touching the sprite and remain `false` if it isn't
324 | let hit = false;
325 |
326 | //Find out the sprite's offset from its anchor point
327 | let xAnchorOffset, yAnchorOffset;
328 | if (sprite.anchor !== undefined) {
329 | xAnchorOffset = sprite.width * sprite.anchor.x;
330 | yAnchorOffset = sprite.height * sprite.anchor.y;
331 | } else {
332 | xAnchorOffset = 0;
333 | yAnchorOffset = 0;
334 | }
335 |
336 | //Is the sprite rectangular?
337 | if (!sprite.circular) {
338 |
339 | //Get the position of the sprite's edges using global
340 | //coordinates
341 | let left = sprite.gx - xAnchorOffset,
342 | right = sprite.gx + sprite.width - xAnchorOffset,
343 | top = sprite.gy - yAnchorOffset,
344 | bottom = sprite.gy + sprite.height - yAnchorOffset;
345 |
346 | //Find out if the pointer is intersecting the rectangle.
347 | //`hit` will become `true` if the pointer is inside the
348 | //sprite's area
349 | hit = this.x > left && this.x < right && this.y > top && this.y < bottom;
350 | }
351 |
352 | //Is the sprite circular?
353 | else {
354 | //Find the distance between the pointer and the
355 | //center of the circle
356 | let vx = this.x - (sprite.gx + (sprite.width / 2) - xAnchorOffset),
357 | vy = this.y - (sprite.gy + (sprite.width / 2) - yAnchorOffset),
358 | distance = Math.sqrt(vx * vx + vy * vy);
359 |
360 | //The pointer is intersecting the circle if the
361 | //distance is less than the circle's radius
362 | hit = distance < sprite.width / 2;
363 | }
364 | //Check the value of `hit`
365 | return hit;
366 | }
367 | };
368 |
369 | //Bind the events to the handlers
370 | //Mouse events
371 | element.addEventListener(
372 | "mousemove", pointer.moveHandler.bind(pointer), false
373 | );
374 | element.addEventListener(
375 | "mousedown", pointer.downHandler.bind(pointer), false
376 | );
377 |
378 | //Add the `mouseup` event to the `window` to
379 | //catch a mouse button release outside of the canvas area
380 | window.addEventListener(
381 | "mouseup", pointer.upHandler.bind(pointer), false
382 | );
383 |
384 | //Touch events
385 | element.addEventListener(
386 | "touchmove", pointer.touchmoveHandler.bind(pointer), false
387 | );
388 | element.addEventListener(
389 | "touchstart", pointer.touchstartHandler.bind(pointer), false
390 | );
391 |
392 | //Add the `touchend` event to the `window` object to
393 | //catch a mouse button release outside of the canvas area
394 | window.addEventListener(
395 | "touchend", pointer.touchendHandler.bind(pointer), false
396 | );
397 |
398 | //Disable the default pan and zoom actions on the `canvas`
399 | element.style.touchAction = "none";
400 |
401 | //Add the pointer to Tink's global `pointers` array
402 | this.pointers.push(pointer);
403 |
404 | //Return the pointer
405 | return pointer;
406 | }
407 |
408 | //Many of Tink's objects, like pointers, use collision
409 | //detection using the sprites' global x and y positions. To make
410 | //this easier, new `gx` and `gy` properties are added to sprites
411 | //that reference Pixi sprites' `getGlobalPosition()` values.
412 | addGlobalPositionProperties(sprite) {
413 | if (sprite.gx === undefined) {
414 | Object.defineProperty(
415 | sprite,
416 | "gx", {
417 | get() {
418 | return sprite.getGlobalPosition().x;
419 | }
420 | }
421 | );
422 | }
423 |
424 | if (sprite.gy === undefined) {
425 | Object.defineProperty(
426 | sprite,
427 | "gy", {
428 | get() {
429 | return sprite.getGlobalPosition().y;
430 | }
431 | }
432 | );
433 | }
434 | }
435 |
436 | //A method that implments drag-and-drop functionality
437 | //for each pointer
438 | updateDragAndDrop(draggableSprites) {
439 |
440 | //Create a pointer if one doesn't already exist
441 | if (this.pointers.length === 0) {
442 | this.makePointer(this.element, this.scale);
443 | }
444 |
445 | //Loop through all the pointers in Tink's global `pointers` array
446 | //(there will usually just be one, but you never know)
447 | this.pointers.forEach(pointer => {
448 |
449 | //Check whether the pointer is pressed down
450 | if (pointer.isDown) {
451 |
452 | //You need to capture the co-ordinates at which the pointer was
453 | //pressed down and find out if it's touching a sprite
454 |
455 | //Only run pointer.code if the pointer isn't already dragging
456 | //sprite
457 | if (pointer.dragSprite === null) {
458 |
459 | //Loop through the `draggableSprites` in reverse to start searching at the bottom of the stack
460 | for (let i = draggableSprites.length - 1; i > -1; i--) {
461 |
462 | //Get a reference to the current sprite
463 | let sprite = draggableSprites[i];
464 |
465 | //Check for a collision with the pointer using `hitTestSprite`
466 | if (pointer.hitTestSprite(sprite) && sprite.draggable) {
467 |
468 | //Calculate the difference between the pointer's
469 | //position and the sprite's position
470 | pointer.dragOffsetX = pointer.x - sprite.gx;
471 | pointer.dragOffsetY = pointer.y - sprite.gy;
472 |
473 | //Set the sprite as the pointer's `dragSprite` property
474 | pointer.dragSprite = sprite;
475 |
476 | //The next two lines re-order the `sprites` array so that the
477 | //selected sprite is displayed above all the others.
478 | //First, splice the sprite out of its current position in
479 | //its parent's `children` array
480 | let children = sprite.parent.children;
481 | children.splice(children.indexOf(sprite), 1);
482 |
483 | //Next, push the `dragSprite` to the end of its `children` array so that it's
484 | //displayed last, above all the other sprites
485 | children.push(sprite);
486 |
487 | //Reorganize the `draggableSpites` array in the same way
488 | draggableSprites.splice(draggableSprites.indexOf(sprite), 1);
489 | draggableSprites.push(sprite);
490 |
491 | //Break the loop, because we only need to drag the topmost sprite
492 | break;
493 | }
494 | }
495 | }
496 |
497 | //If the pointer is down and it has a `dragSprite`, make the sprite follow the pointer's
498 | //position, with the calculated offset
499 | else {
500 | pointer.dragSprite.x = pointer.x - pointer.dragOffsetX;
501 | pointer.dragSprite.y = pointer.y - pointer.dragOffsetY;
502 | }
503 | }
504 |
505 | //If the pointer is up, drop the `dragSprite` by setting it to `null`
506 | if (pointer.isUp) {
507 | pointer.dragSprite = null;
508 | }
509 |
510 | //Change the mouse arrow pointer to a hand if it's over a
511 | //draggable sprite
512 | draggableSprites.some(sprite => {
513 | if (pointer.hitTestSprite(sprite) && sprite.draggable) {
514 | if (pointer.visible) pointer.cursor = "pointer";
515 | return true;
516 | } else {
517 | if (pointer.visible) pointer.cursor = "auto";
518 | return false;
519 | }
520 | });
521 | });
522 | }
523 |
524 | makeInteractive(o) {
525 |
526 | //The `press`,`release`, `over`, `out` and `tap` methods. They're `undefined`
527 | //for now, but they can be defined in the game program
528 | o.press = o.press || undefined;
529 | o.release = o.release || undefined;
530 | o.over = o.over || undefined;
531 | o.out = o.out || undefined;
532 | o.tap = o.tap || undefined;
533 |
534 | //The `state` property tells you the button's
535 | //current state. Set its initial state to "up"
536 | o.state = "up";
537 |
538 | //The `action` property tells you whether its being pressed or
539 | //released
540 | o.action = "";
541 |
542 | //The `pressed` and `hoverOver` Booleans are mainly for internal
543 | //use in this code to help figure out the correct state.
544 | //`pressed` is a Boolean that helps track whether or not
545 | //the sprite has been pressed down
546 | o.pressed = false;
547 |
548 | //`hoverOver` is a Boolean which checks whether the pointer
549 | //has hovered over the sprite
550 | o.hoverOver = false;
551 |
552 | //tinkType is a string that will be set to "button" if the
553 | //user creates an object using the `button` function
554 | o.tinkType = "";
555 |
556 | //Set `enabled` to true to allow for interactivity
557 | //Set `enabled` to false to disable interactivity
558 | o.enabled = true;
559 |
560 | //Add the sprite to the global `buttons` array so that it can
561 | //be updated each frame in the `updateButtons method
562 | this.buttons.push(o);
563 | }
564 |
565 | //The `updateButtons` method will be called each frame
566 | //inside the game loop. It updates all the button-like sprites
567 | updateButtons() {
568 |
569 | //Create a pointer if one doesn't already exist
570 | if (this.pointers.length === 0) {
571 | this.makePointer(this.element, this.scale);
572 | }
573 |
574 | //Loop through all of Tink's pointers (there will usually
575 | //just be one)
576 | this.pointers.forEach(pointer => {
577 |
578 | pointer.shouldBeHand = false;
579 |
580 |
581 |
582 | //Loop through all the button-like sprites that were created
583 | //using the `makeInteractive` method
584 | this.buttons.forEach(o => {
585 |
586 | //Only do this if the interactive object is enabled
587 | if (o.enabled) {
588 |
589 | //Figure out if the pointer is touching the sprite
590 | let hit = pointer.hitTestSprite(o);
591 |
592 | //1. Figure out the current state
593 | if (pointer.isUp) {
594 |
595 | //Up state
596 | o.state = "up";
597 |
598 | //Show the first image state frame, if this is a `Button` sprite
599 | if (o.tinkType === "button") o.gotoAndStop(0);
600 | }
601 |
602 | //If the pointer is touching the sprite, figure out
603 | //if the over or down state should be displayed
604 | if (hit) {
605 |
606 | //Over state
607 | o.state = "over";
608 |
609 | //Show the second image state frame if this sprite has
610 | //3 frames and it's a `Button` sprite
611 | if (o.totalFrames && o.totalFrames === 3 && o.tinkType === "button") {
612 | o.gotoAndStop(1);
613 | }
614 |
615 | //Down state
616 | if (pointer.isDown) {
617 | o.state = "down";
618 |
619 | //Show the third frame if this sprite is a `Button` sprite and it
620 | //has only three frames, or show the second frame if it
621 | //only has two frames
622 | if (o.tinkType === "button") {
623 | if (o.totalFrames === 3) {
624 | o.gotoAndStop(2);
625 | } else {
626 | o.gotoAndStop(1);
627 | }
628 | }
629 | }
630 |
631 |
632 |
633 | //Flag this pointer to be changed to a hand
634 | pointer.shouldBeHand = true;
635 | //if (pointer.visible) pointer.cursor = "pointer";
636 | // } else {
637 | // //Turn the pointer to an ordinary arrow icon if the
638 | // //pointer isn't touching a sprite
639 | // if (pointer.visible) pointer.cursor = "auto";
640 |
641 | //Change the pointer icon to a hand
642 | if (pointer.visible) pointer.cursor = "pointer";
643 | } else {
644 | //Turn the pointer to an ordinary arrow icon if the
645 | //pointer isn't touching a sprite
646 | if (pointer.visible) pointer.cursor = "auto";
647 |
648 | }
649 |
650 | //Perform the correct interactive action
651 |
652 | //a. Run the `press` method if the sprite state is "down" and
653 | //the sprite hasn't already been pressed
654 | if (o.state === "down") {
655 | if (!o.pressed) {
656 | if (o.press) o.press();
657 | o.pressed = true;
658 | o.action = "pressed";
659 | }
660 | }
661 |
662 | //b. Run the `release` method if the sprite state is "over" and
663 | //the sprite has been pressed
664 | if (o.state === "over") {
665 | if (o.pressed) {
666 | if (o.release) o.release();
667 | o.pressed = false;
668 | o.action = "released";
669 | //If the pointer was tapped and the user assigned a `tap`
670 | //method, call the `tap` method
671 | if (pointer.tapped && o.tap) o.tap();
672 | }
673 |
674 | //Run the `over` method if it has been assigned
675 | if (!o.hoverOver) {
676 | if (o.over) o.over();
677 | o.hoverOver = true;
678 | }
679 | }
680 |
681 | //c. Check whether the pointer has been released outside
682 | //the sprite's area. If the button state is "up" and it's
683 | //already been pressed, then run the `release` method.
684 | if (o.state === "up") {
685 | if (o.pressed) {
686 | if (o.release) o.release();
687 | o.pressed = false;
688 | o.action = "released";
689 | }
690 |
691 | //Run the `out` method if it has been assigned
692 | if (o.hoverOver) {
693 | if (o.out) o.out();
694 | o.hoverOver = false;
695 | }
696 | }
697 | }
698 | });
699 |
700 | if (pointer.shouldBeHand) {
701 | pointer.cursor = "pointer";
702 | } else {
703 | pointer.cursor = "auto";
704 | }
705 |
706 |
707 | });
708 | }
709 |
710 | //A function that creates a sprite with 3 frames that
711 | //represent the button states: up, over and down
712 | button(source, x = 0, y = 0) {
713 |
714 | //The sprite object that will be returned
715 | let o;
716 |
717 | //Is it an array of frame ids or textures?
718 | if (typeof source[0] === "string") {
719 |
720 | //They're strings, but are they pre-existing texture or
721 | //paths to image files?
722 | //Check to see if the first element matches a texture in the
723 | //cache
724 | if (this.TextureCache[source[0]]) {
725 |
726 | //It does, so it's an array of frame ids
727 | o = this.AnimatedSprite.fromFrames(source);
728 | } else {
729 |
730 | //It's not already in the cache, so let's load it
731 | o = this.AnimatedSprite.fromImages(source);
732 | }
733 | }
734 |
735 | //If the `source` isn't an array of strings, check whether
736 | //it's an array of textures
737 | else if (source[0] instanceof this.Texture) {
738 |
739 | //Yes, it's an array of textures.
740 | //Use them to make a AnimatedSprite o
741 | o = new this.AnimatedSprite(source);
742 | }
743 |
744 | //Add interactive properties to the button
745 | this.makeInteractive(o);
746 |
747 | //Set the `tinkType` to "button"
748 | o.tinkType = "button";
749 |
750 | //Position the button
751 | o.x = x;
752 | o.y = y;
753 |
754 | //Return the new button sprite
755 | return o;
756 | }
757 |
758 | //Run the `udpate` function in your game loop
759 | //to update all of Tink's interactive objects
760 | update() {
761 |
762 | //Update the drag and drop system
763 | if (this.draggableSprites.length !== 0) this.updateDragAndDrop(this.draggableSprites);
764 |
765 | //Update the buttons and button-like interactive sprites
766 | if (this.buttons.length !== 0) this.updateButtons();
767 | }
768 |
769 | /*
770 | `keyboard` is a method that listens for and captures keyboard events. It's really
771 | just a convenient wrapper function for HTML `keyup` and `keydown` events so that you can keep your application code clutter-free and easier to write and read.
772 |
773 | Here's how to use the `keyboard` method. Create a new keyboard object like this:
774 | ```js
775 | let keyObject = keyboard(asciiKeyCodeNumber);
776 | ```
777 | It's one argument is the ASCII key code number of the keyboard key
778 | that you want to listen for. [Here's a list of ASCII key codes you can
779 | use](http://www.asciitable.com).
780 | Then assign `press` and `release` methods to the keyboard object like this:
781 | ```js
782 | keyObject.press = () => {
783 | //key object pressed
784 | };
785 | keyObject.release = () => {
786 | //key object released
787 | };
788 | ```
789 | Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to check the state of each key.
790 | */
791 | keyboard(keyCode) {
792 | let key = {};
793 | key.code = keyCode;
794 | key.isDown = false;
795 | key.isUp = true;
796 | key.press = undefined;
797 | key.release = undefined;
798 |
799 | //The `downHandler`
800 | key.downHandler = event => {
801 | if (event.keyCode === key.code) {
802 | if (key.isUp && key.press) key.press();
803 | key.isDown = true;
804 | key.isUp = false;
805 | }
806 | event.preventDefault();
807 | };
808 |
809 | //The `upHandler`
810 | key.upHandler = event => {
811 | if (event.keyCode === key.code) {
812 | if (key.isDown && key.release) key.release();
813 | key.isDown = false;
814 | key.isUp = true;
815 | }
816 | event.preventDefault();
817 | };
818 |
819 | //Attach event listeners
820 | window.addEventListener(
821 | "keydown", key.downHandler.bind(key), false
822 | );
823 | window.addEventListener(
824 | "keyup", key.upHandler.bind(key), false
825 | );
826 |
827 | //Return the key object
828 | return key;
829 | }
830 |
831 | //`arrowControl` is a convenience method for updating a sprite's velocity
832 | //for 4-way movement using the arrow directional keys. Supply it
833 | //with the sprite you want to control and the speed per frame, in
834 | //pixels, that you want to update the sprite's velocity
835 | arrowControl(sprite, speed) {
836 |
837 | if (speed === undefined) {
838 | throw new Error("Please supply the arrowControl method with the speed at which you want the sprite to move");
839 | }
840 |
841 | let upArrow = this.keyboard(38),
842 | rightArrow = this.keyboard(39),
843 | downArrow = this.keyboard(40),
844 | leftArrow = this.keyboard(37);
845 |
846 | //Assign key `press` methods
847 | leftArrow.press = () => {
848 | //Change the sprite's velocity when the key is pressed
849 | sprite.vx = -speed;
850 | sprite.vy = 0;
851 | };
852 | leftArrow.release = () => {
853 | //If the left arrow has been released, and the right arrow isn't down,
854 | //and the sprite isn't moving vertically:
855 | //Stop the sprite
856 | if (!rightArrow.isDown && sprite.vy === 0) {
857 | sprite.vx = 0;
858 | }
859 | };
860 | upArrow.press = () => {
861 | sprite.vy = -speed;
862 | sprite.vx = 0;
863 | };
864 | upArrow.release = () => {
865 | if (!downArrow.isDown && sprite.vx === 0) {
866 | sprite.vy = 0;
867 | }
868 | };
869 | rightArrow.press = () => {
870 | sprite.vx = speed;
871 | sprite.vy = 0;
872 | };
873 | rightArrow.release = () => {
874 | if (!leftArrow.isDown && sprite.vy === 0) {
875 | sprite.vx = 0;
876 | }
877 | };
878 | downArrow.press = () => {
879 | sprite.vy = speed;
880 | sprite.vx = 0;
881 | };
882 | downArrow.release = () => {
883 | if (!upArrow.isDown && sprite.vx === 0) {
884 | sprite.vy = 0;
885 | }
886 | };
887 | }
888 | }
--------------------------------------------------------------------------------
/bin/tink.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4 |
5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6 |
7 | var Tink = (function () {
8 | function Tink(PIXI, element) {
9 | var scale = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
10 |
11 | _classCallCheck(this, Tink);
12 |
13 | //Add element and scale properties
14 | this.element = element;
15 | this._scale = scale;
16 |
17 | //An array to store all the draggable sprites
18 | this.draggableSprites = [];
19 |
20 | //An array to store all the pointer objects
21 | //(there will usually just be one)
22 | this.pointers = [];
23 |
24 | //An array to store all the buttons and button-like
25 | //interactive sprites
26 | this.buttons = [];
27 |
28 | //A local PIXI reference
29 | this.PIXI = PIXI;
30 |
31 | //Aliases for Pixi objects
32 | this.TextureCache = this.PIXI.utils.TextureCache;
33 |
34 | //Note: change MovieClip to AnimatedSprite for Pixi v4
35 | this.AnimatedSprite = this.PIXI.extras.MovieClip;
36 | this.Texture = this.PIXI.Texture;
37 | }
38 |
39 | _createClass(Tink, [{
40 | key: "makeDraggable",
41 |
42 | //`makeDraggable` lets you make a drag-and-drop sprite by pushing it
43 | //into the `draggableSprites` array
44 | value: function makeDraggable() {
45 | var _this = this;
46 |
47 | for (var _len = arguments.length, sprites = Array(_len), _key = 0; _key < _len; _key++) {
48 | sprites[_key] = arguments[_key];
49 | }
50 |
51 | //If the first argument isn't an array of sprites...
52 | if (!(sprites[0] instanceof Array)) {
53 | sprites.forEach(function (sprite) {
54 | _this.draggableSprites.push(sprite);
55 |
56 | //If the sprite's `draggable` property hasn't already been defined by
57 | //another library, like Hexi, define it
58 | if (sprite.draggable === undefined) {
59 | sprite.draggable = true;
60 | sprite._localDraggableAllocation = true;
61 | }
62 | });
63 | }
64 |
65 | //If the first argument is an array of sprites...
66 | else {
67 | var spritesArray = sprites[0];
68 | if (spritesArray.length > 0) {
69 | for (var i = spritesArray.length - 1; i >= 0; i--) {
70 | var sprite = spritesArray[i];
71 | this.draggableSprites.push(sprite);
72 |
73 | //If the sprite's `draggable` property hasn't already been defined by
74 | //another library, like Hexi, define it
75 | if (sprite.draggable === undefined) {
76 | sprite.draggable = true;
77 | sprite._localDraggableAllocation = true;
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
84 | //`makeUndraggable` removes the sprite from the `draggableSprites`
85 | //array
86 |
87 | }, {
88 | key: "makeUndraggable",
89 | value: function makeUndraggable() {
90 | var _this2 = this;
91 |
92 | for (var _len2 = arguments.length, sprites = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
93 | sprites[_key2] = arguments[_key2];
94 | }
95 |
96 | //If the first argument isn't an array of sprites...
97 | if (!(sprites[0] instanceof Array)) {
98 | sprites.forEach(function (sprite) {
99 | _this2.draggableSprites.splice(_this2.draggableSprites.indexOf(sprite), 1);
100 | if (sprite._localDraggableAllocation === true) sprite.draggable = false;
101 | });
102 | }
103 |
104 | //If the first argument is an array of sprites
105 | else {
106 | var spritesArray = sprites[0];
107 | if (spritesArray.length > 0) {
108 | for (var i = spritesArray.length - 1; i >= 0; i--) {
109 | var sprite = spritesArray[i];
110 | this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);
111 | if (sprite._localDraggableAllocation === true) sprite.draggable = false;
112 | }
113 | }
114 | }
115 | }
116 | }, {
117 | key: "makePointer",
118 | value: function makePointer() {
119 | var element = arguments.length <= 0 || arguments[0] === undefined ? this.element : arguments[0];
120 | var scale = arguments.length <= 1 || arguments[1] === undefined ? this.scale : arguments[1];
121 |
122 | //Get a reference to Tink's global `draggableSprites` array
123 | var draggableSprites = this.draggableSprites;
124 |
125 | //Get a reference to Tink's `addGlobalPositionProperties` method
126 | var addGlobalPositionProperties = this.addGlobalPositionProperties;
127 |
128 | //The pointer object will be returned by this function
129 | var pointer = {
130 | element: element,
131 | _scale: scale,
132 |
133 | //Private x and y properties
134 | _x: 0,
135 | _y: 0,
136 |
137 | //Width and height
138 | width: 1,
139 | height: 1,
140 |
141 | //The public x and y properties are divided by the scale. If the
142 | //HTML element that the pointer is sensitive to (like the canvas)
143 | //is scaled up or down, you can change the `scale` value to
144 | //correct the pointer's position values
145 | get x() {
146 | return this._x / this.scale;
147 | },
148 | get y() {
149 | return this._y / this.scale;
150 | },
151 |
152 | //Add `centerX` and `centerY` getters so that we
153 | //can use the pointer's coordinates with easing
154 | //and collision functions
155 | get centerX() {
156 | return this.x;
157 | },
158 | get centerY() {
159 | return this.y;
160 | },
161 |
162 | //`position` returns an object with x and y properties that
163 | //contain the pointer's position
164 | get position() {
165 | return {
166 | x: this.x,
167 | y: this.y
168 | };
169 | },
170 |
171 | get scale() {
172 | return this._scale;
173 | },
174 | set scale(value) {
175 | this._scale = value;
176 | },
177 |
178 | //Add a `cursor` getter/setter to change the pointer's cursor
179 | //style. Values can be "pointer" (for a hand icon) or "auto" for
180 | //an ordinary arrow icon.
181 | get cursor() {
182 | return this.element.style.cursor;
183 | },
184 | set cursor(value) {
185 | this.element.style.cursor = value;
186 | },
187 |
188 | //Booleans to track the pointer state
189 | isDown: false,
190 | isUp: true,
191 | tapped: false,
192 |
193 | //Properties to help measure the time between up and down states
194 | downTime: 0,
195 | elapsedTime: 0,
196 |
197 | //Optional `press`,`release` and `tap` methods
198 | press: undefined,
199 | release: undefined,
200 | tap: undefined,
201 |
202 | //A `dragSprite` property to help with drag and drop
203 | dragSprite: null,
204 |
205 | //The drag offsets to help drag sprites
206 | dragOffsetX: 0,
207 | dragOffsetY: 0,
208 |
209 | //A property to check whether or not the pointer
210 | //is visible
211 | _visible: true,
212 | get visible() {
213 | return this._visible;
214 | },
215 | set visible(value) {
216 | if (value === true) {
217 | this.cursor = "auto";
218 | } else {
219 | this.cursor = "none";
220 | }
221 | this._visible = value;
222 | },
223 |
224 | //The pointer's mouse `moveHandler`
225 | moveHandler: function moveHandler(event) {
226 |
227 | //Get the element that's firing the event
228 | var element = event.target;
229 |
230 | //Find the pointer’s x and y position (for mouse).
231 | //Subtract the element's top and left offset from the browser window
232 | this._x = event.pageX - element.offsetLeft;
233 | this._y = event.pageY - element.offsetTop;
234 |
235 | //Prevent the event's default behavior
236 | event.preventDefault();
237 | },
238 |
239 | //The pointer's `touchmoveHandler`
240 | touchmoveHandler: function touchmoveHandler(event) {
241 | var element = event.target;
242 |
243 | //Find the touch point's x and y position
244 | this._x = event.targetTouches[0].pageX - element.offsetLeft;
245 | this._y = event.targetTouches[0].pageY - element.offsetTop;
246 | event.preventDefault();
247 | },
248 |
249 | //The pointer's `downHandler`
250 | downHandler: function downHandler(event) {
251 |
252 | //Set the down states
253 | this.isDown = true;
254 | this.isUp = false;
255 | this.tapped = false;
256 |
257 | //Capture the current time
258 | this.downTime = Date.now();
259 |
260 | //Call the `press` method if it's been assigned
261 | if (this.press) this.press();
262 | event.preventDefault();
263 | },
264 |
265 | //The pointer's `touchstartHandler`
266 | touchstartHandler: function touchstartHandler(event) {
267 | var element = event.target;
268 |
269 | //Find the touch point's x and y position
270 | this._x = event.targetTouches[0].pageX - element.offsetLeft;
271 | this._y = event.targetTouches[0].pageY - element.offsetTop;
272 |
273 | //Set the down states
274 | this.isDown = true;
275 | this.isUp = false;
276 | this.tapped = false;
277 |
278 | //Capture the current time
279 | this.downTime = Date.now();
280 |
281 | //Call the `press` method if it's been assigned
282 | if (this.press) this.press();
283 | event.preventDefault();
284 | },
285 |
286 | //The pointer's `upHandler`
287 | upHandler: function upHandler(event) {
288 |
289 | //Figure out how much time the pointer has been down
290 | this.elapsedTime = Math.abs(this.downTime - Date.now());
291 |
292 | //If it's less than 200 milliseconds, it must be a tap or click
293 | if (this.elapsedTime <= 200 && this.tapped === false) {
294 | this.tapped = true;
295 |
296 | //Call the `tap` method if it's been assigned
297 | if (this.tap) this.tap();
298 | }
299 | this.isUp = true;
300 | this.isDown = false;
301 |
302 | //Call the `release` method if it's been assigned
303 | if (this.release) this.release();
304 |
305 | //`event.preventDefault();` needs to be disabled to prevent range sliders
306 | //from getting trapped in Firefox (and possibly Safari)
307 | //event.preventDefault();
308 | },
309 |
310 | //The pointer's `touchendHandler`
311 | touchendHandler: function touchendHandler(event) {
312 |
313 | //Figure out how much time the pointer has been down
314 | this.elapsedTime = Math.abs(this.downTime - Date.now());
315 |
316 | //If it's less than 200 milliseconds, it must be a tap or click
317 | if (this.elapsedTime <= 200 && this.tapped === false) {
318 | this.tapped = true;
319 |
320 | //Call the `tap` method if it's been assigned
321 | if (this.tap) this.tap();
322 | }
323 | this.isUp = true;
324 | this.isDown = false;
325 |
326 | //Call the `release` method if it's been assigned
327 | if (this.release) this.release();
328 |
329 | //event.preventDefault();
330 | },
331 |
332 | //`hitTestSprite` figures out if the pointer is touching a sprite
333 | hitTestSprite: function hitTestSprite(sprite) {
334 |
335 | //Add global `gx` and `gy` properties to the sprite if they
336 | //don't already exist
337 | addGlobalPositionProperties(sprite);
338 |
339 | //The `hit` variable will become `true` if the pointer is
340 | //touching the sprite and remain `false` if it isn't
341 | var hit = false;
342 |
343 | //Find out the sprite's offset from its anchor point
344 | var xAnchorOffset = undefined,
345 | yAnchorOffset = undefined;
346 | if (sprite.anchor !== undefined) {
347 | xAnchorOffset = sprite.width * sprite.anchor.x;
348 | yAnchorOffset = sprite.height * sprite.anchor.y;
349 | } else {
350 | xAnchorOffset = 0;
351 | yAnchorOffset = 0;
352 | }
353 |
354 | //Is the sprite rectangular?
355 | if (!sprite.circular) {
356 |
357 | //Get the position of the sprite's edges using global
358 | //coordinates
359 | var left = sprite.gx - xAnchorOffset,
360 | right = sprite.gx + sprite.width - xAnchorOffset,
361 | top = sprite.gy - yAnchorOffset,
362 | bottom = sprite.gy + sprite.height - yAnchorOffset;
363 |
364 | //Find out if the pointer is intersecting the rectangle.
365 | //`hit` will become `true` if the pointer is inside the
366 | //sprite's area
367 | hit = this.x > left && this.x < right && this.y > top && this.y < bottom;
368 | }
369 |
370 | //Is the sprite circular?
371 | else {
372 | //Find the distance between the pointer and the
373 | //center of the circle
374 | var vx = this.x - (sprite.gx + sprite.width / 2 - xAnchorOffset),
375 | vy = this.y - (sprite.gy + sprite.width / 2 - yAnchorOffset),
376 | distance = Math.sqrt(vx * vx + vy * vy);
377 |
378 | //The pointer is intersecting the circle if the
379 | //distance is less than the circle's radius
380 | hit = distance < sprite.width / 2;
381 | }
382 | //Check the value of `hit`
383 | return hit;
384 | }
385 | };
386 |
387 | //Bind the events to the handlers
388 | //Mouse events
389 | element.addEventListener("mousemove", pointer.moveHandler.bind(pointer), false);
390 | element.addEventListener("mousedown", pointer.downHandler.bind(pointer), false);
391 |
392 | //Add the `mouseup` event to the `window` to
393 | //catch a mouse button release outside of the canvas area
394 | window.addEventListener("mouseup", pointer.upHandler.bind(pointer), false);
395 |
396 | //Touch events
397 | element.addEventListener("touchmove", pointer.touchmoveHandler.bind(pointer), false);
398 | element.addEventListener("touchstart", pointer.touchstartHandler.bind(pointer), false);
399 |
400 | //Add the `touchend` event to the `window` object to
401 | //catch a mouse button release outside of the canvas area
402 | window.addEventListener("touchend", pointer.touchendHandler.bind(pointer), false);
403 |
404 | //Disable the default pan and zoom actions on the `canvas`
405 | element.style.touchAction = "none";
406 |
407 | //Add the pointer to Tink's global `pointers` array
408 | this.pointers.push(pointer);
409 |
410 | //Return the pointer
411 | return pointer;
412 | }
413 |
414 | //Many of Tink's objects, like pointers, use collision
415 | //detection using the sprites' global x and y positions. To make
416 | //this easier, new `gx` and `gy` properties are added to sprites
417 | //that reference Pixi sprites' `getGlobalPosition()` values.
418 |
419 | }, {
420 | key: "addGlobalPositionProperties",
421 | value: function addGlobalPositionProperties(sprite) {
422 | if (sprite.gx === undefined) {
423 | Object.defineProperty(sprite, "gx", {
424 | get: function get() {
425 | return sprite.getGlobalPosition().x;
426 | }
427 | });
428 | }
429 |
430 | if (sprite.gy === undefined) {
431 | Object.defineProperty(sprite, "gy", {
432 | get: function get() {
433 | return sprite.getGlobalPosition().y;
434 | }
435 | });
436 | }
437 | }
438 |
439 | //A method that implments drag-and-drop functionality
440 | //for each pointer
441 |
442 | }, {
443 | key: "updateDragAndDrop",
444 | value: function updateDragAndDrop(draggableSprites) {
445 |
446 | //Create a pointer if one doesn't already exist
447 | if (this.pointers.length === 0) {
448 | this.makePointer(this.element, this.scale);
449 | }
450 |
451 | //Loop through all the pointers in Tink's global `pointers` array
452 | //(there will usually just be one, but you never know)
453 | this.pointers.forEach(function (pointer) {
454 |
455 | //Check whether the pointer is pressed down
456 | if (pointer.isDown) {
457 |
458 | //You need to capture the co-ordinates at which the pointer was
459 | //pressed down and find out if it's touching a sprite
460 |
461 | //Only run pointer.code if the pointer isn't already dragging
462 | //sprite
463 | if (pointer.dragSprite === null) {
464 |
465 | //Loop through the `draggableSprites` in reverse to start searching at the bottom of the stack
466 | for (var i = draggableSprites.length - 1; i > -1; i--) {
467 |
468 | //Get a reference to the current sprite
469 | var sprite = draggableSprites[i];
470 |
471 | //Check for a collision with the pointer using `hitTestSprite`
472 | if (pointer.hitTestSprite(sprite) && sprite.draggable) {
473 |
474 | //Calculate the difference between the pointer's
475 | //position and the sprite's position
476 | pointer.dragOffsetX = pointer.x - sprite.gx;
477 | pointer.dragOffsetY = pointer.y - sprite.gy;
478 |
479 | //Set the sprite as the pointer's `dragSprite` property
480 | pointer.dragSprite = sprite;
481 |
482 | //The next two lines re-order the `sprites` array so that the
483 | //selected sprite is displayed above all the others.
484 | //First, splice the sprite out of its current position in
485 | //its parent's `children` array
486 | var children = sprite.parent.children;
487 | children.splice(children.indexOf(sprite), 1);
488 |
489 | //Next, push the `dragSprite` to the end of its `children` array so that it's
490 | //displayed last, above all the other sprites
491 | children.push(sprite);
492 |
493 | //Reorganize the `draggableSpites` array in the same way
494 | draggableSprites.splice(draggableSprites.indexOf(sprite), 1);
495 | draggableSprites.push(sprite);
496 |
497 | //Break the loop, because we only need to drag the topmost sprite
498 | break;
499 | }
500 | }
501 | }
502 |
503 | //If the pointer is down and it has a `dragSprite`, make the sprite follow the pointer's
504 | //position, with the calculated offset
505 | else {
506 | pointer.dragSprite.x = pointer.x - pointer.dragOffsetX;
507 | pointer.dragSprite.y = pointer.y - pointer.dragOffsetY;
508 | }
509 | }
510 |
511 | //If the pointer is up, drop the `dragSprite` by setting it to `null`
512 | if (pointer.isUp) {
513 | pointer.dragSprite = null;
514 | }
515 |
516 | //Change the mouse arrow pointer to a hand if it's over a
517 | //draggable sprite
518 | draggableSprites.some(function (sprite) {
519 | if (pointer.hitTestSprite(sprite) && sprite.draggable) {
520 | if (pointer.visible) pointer.cursor = "pointer";
521 | return true;
522 | } else {
523 | if (pointer.visible) pointer.cursor = "auto";
524 | return false;
525 | }
526 | });
527 | });
528 | }
529 | }, {
530 | key: "makeInteractive",
531 | value: function makeInteractive(o) {
532 |
533 | //The `press`,`release`, `over`, `out` and `tap` methods. They're `undefined`
534 | //for now, but they can be defined in the game program
535 | o.press = o.press || undefined;
536 | o.release = o.release || undefined;
537 | o.over = o.over || undefined;
538 | o.out = o.out || undefined;
539 | o.tap = o.tap || undefined;
540 |
541 | //The `state` property tells you the button's
542 | //current state. Set its initial state to "up"
543 | o.state = "up";
544 |
545 | //The `action` property tells you whether its being pressed or
546 | //released
547 | o.action = "";
548 |
549 | //The `pressed` and `hoverOver` Booleans are mainly for internal
550 | //use in this code to help figure out the correct state.
551 | //`pressed` is a Boolean that helps track whether or not
552 | //the sprite has been pressed down
553 | o.pressed = false;
554 |
555 | //`hoverOver` is a Boolean which checks whether the pointer
556 | //has hovered over the sprite
557 | o.hoverOver = false;
558 |
559 | //tinkType is a string that will be set to "button" if the
560 | //user creates an object using the `button` function
561 | o.tinkType = "";
562 |
563 | //Set `enabled` to true to allow for interactivity
564 | //Set `enabled` to false to disable interactivity
565 | o.enabled = true;
566 |
567 | //Add the sprite to the global `buttons` array so that it can
568 | //be updated each frame in the `updateButtons method
569 | this.buttons.push(o);
570 | }
571 |
572 | //The `updateButtons` method will be called each frame
573 | //inside the game loop. It updates all the button-like sprites
574 |
575 | }, {
576 | key: "updateButtons",
577 | value: function updateButtons() {
578 | var _this3 = this;
579 |
580 | //Create a pointer if one doesn't already exist
581 | if (this.pointers.length === 0) {
582 | this.makePointer(this.element, this.scale);
583 | }
584 |
585 | //Loop through all of Tink's pointers (there will usually
586 | //just be one)
587 | this.pointers.forEach(function (pointer) {
588 |
589 | pointer.shouldBeHand = false;
590 |
591 | //Loop through all the button-like sprites that were created
592 | //using the `makeInteractive` method
593 | _this3.buttons.forEach(function (o) {
594 |
595 | //Only do this if the interactive object is enabled
596 | if (o.enabled) {
597 |
598 | //Figure out if the pointer is touching the sprite
599 | var hit = pointer.hitTestSprite(o);
600 |
601 | //1. Figure out the current state
602 | if (pointer.isUp) {
603 |
604 | //Up state
605 | o.state = "up";
606 |
607 | //Show the first image state frame, if this is a `Button` sprite
608 | if (o.tinkType === "button") o.gotoAndStop(0);
609 | }
610 |
611 | //If the pointer is touching the sprite, figure out
612 | //if the over or down state should be displayed
613 | if (hit) {
614 |
615 | //Over state
616 | o.state = "over";
617 |
618 | //Show the second image state frame if this sprite has
619 | //3 frames and it's a `Button` sprite
620 | if (o.totalFrames && o.totalFrames === 3 && o.tinkType === "button") {
621 | o.gotoAndStop(1);
622 | }
623 |
624 | //Down state
625 | if (pointer.isDown) {
626 | o.state = "down";
627 |
628 | //Show the third frame if this sprite is a `Button` sprite and it
629 | //has only three frames, or show the second frame if it
630 | //only has two frames
631 | if (o.tinkType === "button") {
632 | if (o.totalFrames === 3) {
633 | o.gotoAndStop(2);
634 | } else {
635 | o.gotoAndStop(1);
636 | }
637 | }
638 | }
639 |
640 | //Flag this pointer to be changed to a hand
641 | pointer.shouldBeHand = true;
642 | //if (pointer.visible) pointer.cursor = "pointer";
643 | // } else {
644 | // //Turn the pointer to an ordinary arrow icon if the
645 | // //pointer isn't touching a sprite
646 | // if (pointer.visible) pointer.cursor = "auto";
647 |
648 | //Change the pointer icon to a hand
649 | if (pointer.visible) pointer.cursor = "pointer";
650 | } else {
651 | //Turn the pointer to an ordinary arrow icon if the
652 | //pointer isn't touching a sprite
653 | if (pointer.visible) pointer.cursor = "auto";
654 | }
655 |
656 | //Perform the correct interactive action
657 |
658 | //a. Run the `press` method if the sprite state is "down" and
659 | //the sprite hasn't already been pressed
660 | if (o.state === "down") {
661 | if (!o.pressed) {
662 | if (o.press) o.press();
663 | o.pressed = true;
664 | o.action = "pressed";
665 | }
666 | }
667 |
668 | //b. Run the `release` method if the sprite state is "over" and
669 | //the sprite has been pressed
670 | if (o.state === "over") {
671 | if (o.pressed) {
672 | if (o.release) o.release();
673 | o.pressed = false;
674 | o.action = "released";
675 | //If the pointer was tapped and the user assigned a `tap`
676 | //method, call the `tap` method
677 | if (pointer.tapped && o.tap) o.tap();
678 | }
679 |
680 | //Run the `over` method if it has been assigned
681 | if (!o.hoverOver) {
682 | if (o.over) o.over();
683 | o.hoverOver = true;
684 | }
685 | }
686 |
687 | //c. Check whether the pointer has been released outside
688 | //the sprite's area. If the button state is "up" and it's
689 | //already been pressed, then run the `release` method.
690 | if (o.state === "up") {
691 | if (o.pressed) {
692 | if (o.release) o.release();
693 | o.pressed = false;
694 | o.action = "released";
695 | }
696 |
697 | //Run the `out` method if it has been assigned
698 | if (o.hoverOver) {
699 | if (o.out) o.out();
700 | o.hoverOver = false;
701 | }
702 | }
703 | }
704 | });
705 |
706 | if (pointer.shouldBeHand) {
707 | pointer.cursor = "pointer";
708 | } else {
709 | pointer.cursor = "auto";
710 | }
711 | });
712 | }
713 |
714 | //A function that creates a sprite with 3 frames that
715 | //represent the button states: up, over and down
716 |
717 | }, {
718 | key: "button",
719 | value: function button(source) {
720 | var x = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
721 | var y = arguments.length <= 2 || arguments[2] === undefined ? 0 : arguments[2];
722 |
723 | //The sprite object that will be returned
724 | var o = undefined;
725 |
726 | //Is it an array of frame ids or textures?
727 | if (typeof source[0] === "string") {
728 |
729 | //They're strings, but are they pre-existing texture or
730 | //paths to image files?
731 | //Check to see if the first element matches a texture in the
732 | //cache
733 | if (this.TextureCache[source[0]]) {
734 |
735 | //It does, so it's an array of frame ids
736 | o = this.AnimatedSprite.fromFrames(source);
737 | } else {
738 |
739 | //It's not already in the cache, so let's load it
740 | o = this.AnimatedSprite.fromImages(source);
741 | }
742 | }
743 |
744 | //If the `source` isn't an array of strings, check whether
745 | //it's an array of textures
746 | else if (source[0] instanceof this.Texture) {
747 |
748 | //Yes, it's an array of textures.
749 | //Use them to make a AnimatedSprite o
750 | o = new this.AnimatedSprite(source);
751 | }
752 |
753 | //Add interactive properties to the button
754 | this.makeInteractive(o);
755 |
756 | //Set the `tinkType` to "button"
757 | o.tinkType = "button";
758 |
759 | //Position the button
760 | o.x = x;
761 | o.y = y;
762 |
763 | //Return the new button sprite
764 | return o;
765 | }
766 |
767 | //Run the `udpate` function in your game loop
768 | //to update all of Tink's interactive objects
769 |
770 | }, {
771 | key: "update",
772 | value: function update() {
773 |
774 | //Update the drag and drop system
775 | if (this.draggableSprites.length !== 0) this.updateDragAndDrop(this.draggableSprites);
776 |
777 | //Update the buttons and button-like interactive sprites
778 | if (this.buttons.length !== 0) this.updateButtons();
779 | }
780 |
781 | /*
782 | `keyboard` is a method that listens for and captures keyboard events. It's really
783 | just a convenient wrapper function for HTML `keyup` and `keydown` events so that you can keep your application code clutter-free and easier to write and read.
784 | Here's how to use the `keyboard` method. Create a new keyboard object like this:
785 | ```js
786 | let keyObject = keyboard(asciiKeyCodeNumber);
787 | ```
788 | It's one argument is the ASCII key code number of the keyboard key
789 | that you want to listen for. [Here's a list of ASCII key codes you can
790 | use](http://www.asciitable.com).
791 | Then assign `press` and `release` methods to the keyboard object like this:
792 | ```js
793 | keyObject.press = () => {
794 | //key object pressed
795 | };
796 | keyObject.release = () => {
797 | //key object released
798 | };
799 | ```
800 | Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to check the state of each key.
801 | */
802 |
803 | }, {
804 | key: "keyboard",
805 | value: function keyboard(keyCode) {
806 | var key = {};
807 | key.code = keyCode;
808 | key.isDown = false;
809 | key.isUp = true;
810 | key.press = undefined;
811 | key.release = undefined;
812 |
813 | //The `downHandler`
814 | key.downHandler = function (event) {
815 | if (event.keyCode === key.code) {
816 | if (key.isUp && key.press) key.press();
817 | key.isDown = true;
818 | key.isUp = false;
819 | }
820 | event.preventDefault();
821 | };
822 |
823 | //The `upHandler`
824 | key.upHandler = function (event) {
825 | if (event.keyCode === key.code) {
826 | if (key.isDown && key.release) key.release();
827 | key.isDown = false;
828 | key.isUp = true;
829 | }
830 | event.preventDefault();
831 | };
832 |
833 | //Attach event listeners
834 | window.addEventListener("keydown", key.downHandler.bind(key), false);
835 | window.addEventListener("keyup", key.upHandler.bind(key), false);
836 |
837 | //Return the key object
838 | return key;
839 | }
840 |
841 | //`arrowControl` is a convenience method for updating a sprite's velocity
842 | //for 4-way movement using the arrow directional keys. Supply it
843 | //with the sprite you want to control and the speed per frame, in
844 | //pixels, that you want to update the sprite's velocity
845 |
846 | }, {
847 | key: "arrowControl",
848 | value: function arrowControl(sprite, speed) {
849 |
850 | if (speed === undefined) {
851 | throw new Error("Please supply the arrowControl method with the speed at which you want the sprite to move");
852 | }
853 |
854 | var upArrow = this.keyboard(38),
855 | rightArrow = this.keyboard(39),
856 | downArrow = this.keyboard(40),
857 | leftArrow = this.keyboard(37);
858 |
859 | //Assign key `press` methods
860 | leftArrow.press = function () {
861 | //Change the sprite's velocity when the key is pressed
862 | sprite.vx = -speed;
863 | sprite.vy = 0;
864 | };
865 | leftArrow.release = function () {
866 | //If the left arrow has been released, and the right arrow isn't down,
867 | //and the sprite isn't moving vertically:
868 | //Stop the sprite
869 | if (!rightArrow.isDown && sprite.vy === 0) {
870 | sprite.vx = 0;
871 | }
872 | };
873 | upArrow.press = function () {
874 | sprite.vy = -speed;
875 | sprite.vx = 0;
876 | };
877 | upArrow.release = function () {
878 | if (!downArrow.isDown && sprite.vx === 0) {
879 | sprite.vy = 0;
880 | }
881 | };
882 | rightArrow.press = function () {
883 | sprite.vx = speed;
884 | sprite.vy = 0;
885 | };
886 | rightArrow.release = function () {
887 | if (!leftArrow.isDown && sprite.vy === 0) {
888 | sprite.vx = 0;
889 | }
890 | };
891 | downArrow.press = function () {
892 | sprite.vy = speed;
893 | sprite.vx = 0;
894 | };
895 | downArrow.release = function () {
896 | if (!upArrow.isDown && sprite.vx === 0) {
897 | sprite.vy = 0;
898 | }
899 | };
900 | }
901 | }, {
902 | key: "scale",
903 | get: function get() {
904 | return this._scale;
905 | },
906 | set: function set(value) {
907 | this._scale = value;
908 |
909 | //Update scale values for all pointers
910 | this.pointers.forEach(function (pointer) {
911 | return pointer.scale = value;
912 | });
913 | }
914 | }]);
915 |
916 | return Tink;
917 | })();
918 | //# sourceMappingURL=tink.js.map
--------------------------------------------------------------------------------
/bin/tink.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/tink.js"],"names":[],"mappings":"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","file":"tink.js","sourcesContent":["class Tink {\n constructor(PIXI, element, scale = 1) {\n\n //Add element and scale properties\n this.element = element;\n this._scale = scale;\n\n //An array to store all the draggable sprites\n this.draggableSprites = [];\n\n //An array to store all the pointer objects\n //(there will usually just be one)\n this.pointers = [];\n\n //An array to store all the buttons and button-like\n //interactive sprites\n this.buttons = [];\n\n //A local PIXI reference\n this.PIXI = PIXI;\n\n //Aliases for Pixi objects\n this.TextureCache = this.PIXI.utils.TextureCache;\n\n //Note: change MovieClip to AnimatedSprite for Pixi v4\n this.AnimatedSprite = this.PIXI.extras.MovieClip;\n this.Texture = this.PIXI.Texture;\n }\n\n get scale() {\n return this._scale;\n }\n\n set scale(value) {\n this._scale = value;\n\n //Update scale values for all pointers\n this.pointers.forEach(pointer => pointer.scale = value);\n }\n\n //`makeDraggable` lets you make a drag-and-drop sprite by pushing it\n //into the `draggableSprites` array\n makeDraggable(...sprites) {\n\n //If the first argument isn't an array of sprites...\n if (!(sprites[0] instanceof Array)) {\n sprites.forEach(sprite => {\n this.draggableSprites.push(sprite);\n\n //If the sprite's `draggable` property hasn't already been defined by\n //another library, like Hexi, define it\n if (sprite.draggable === undefined) {\n sprite.draggable = true;\n sprite._localDraggableAllocation = true;\n }\n });\n }\n\n //If the first argument is an array of sprites...\n else {\n let spritesArray = sprites[0];\n if (spritesArray.length > 0) {\n for (let i = spritesArray.length - 1; i >= 0; i--) {\n let sprite = spritesArray[i];\n this.draggableSprites.push(sprite);\n\n //If the sprite's `draggable` property hasn't already been defined by\n //another library, like Hexi, define it\n if (sprite.draggable === undefined) {\n sprite.draggable = true;\n sprite._localDraggableAllocation = true;\n }\n }\n }\n }\n }\n\n //`makeUndraggable` removes the sprite from the `draggableSprites`\n //array\n makeUndraggable(...sprites) {\n\n //If the first argument isn't an array of sprites...\n if (!(sprites[0] instanceof Array)) {\n sprites.forEach(sprite => {\n this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);\n if (sprite._localDraggableAllocation === true) sprite.draggable = false;\n });\n }\n\n //If the first argument is an array of sprites\n else {\n let spritesArray = sprites[0];\n if (spritesArray.length > 0) {\n for (let i = spritesArray.length - 1; i >= 0; i--) {\n let sprite = spritesArray[i];\n this.draggableSprites.splice(this.draggableSprites.indexOf(sprite), 1);\n if (sprite._localDraggableAllocation === true) sprite.draggable = false;\n }\n }\n }\n }\n\n makePointer(element = this.element, scale = this.scale) {\n\n //Get a reference to Tink's global `draggableSprites` array\n let draggableSprites = this.draggableSprites;\n\n //Get a reference to Tink's `addGlobalPositionProperties` method\n let addGlobalPositionProperties = this.addGlobalPositionProperties;\n\n //The pointer object will be returned by this function\n let pointer = {\n element: element,\n _scale: scale,\n\n //Private x and y properties\n _x: 0,\n _y: 0,\n\n //Width and height\n width: 1,\n height: 1,\n\n //The public x and y properties are divided by the scale. If the\n //HTML element that the pointer is sensitive to (like the canvas)\n //is scaled up or down, you can change the `scale` value to\n //correct the pointer's position values\n get x() {\n return this._x / this.scale;\n },\n get y() {\n return this._y / this.scale;\n },\n\n //Add `centerX` and `centerY` getters so that we\n //can use the pointer's coordinates with easing\n //and collision functions\n get centerX() {\n return this.x;\n },\n get centerY() {\n return this.y;\n },\n\n //`position` returns an object with x and y properties that\n //contain the pointer's position\n get position() {\n return {\n x: this.x,\n y: this.y\n };\n },\n\n get scale() {\n return this._scale;\n },\n set scale(value) {\n this._scale = value;\n },\n\n //Add a `cursor` getter/setter to change the pointer's cursor\n //style. Values can be \"pointer\" (for a hand icon) or \"auto\" for \n //an ordinary arrow icon.\n get cursor() {\n return this.element.style.cursor;\n },\n set cursor(value) {\n this.element.style.cursor = value;\n },\n\n //Booleans to track the pointer state\n isDown: false,\n isUp: true,\n tapped: false,\n\n //Properties to help measure the time between up and down states\n downTime: 0,\n elapsedTime: 0,\n\n //Optional `press`,`release` and `tap` methods\n press: undefined,\n release: undefined,\n tap: undefined,\n\n //A `dragSprite` property to help with drag and drop\n dragSprite: null,\n\n //The drag offsets to help drag sprites\n dragOffsetX: 0,\n dragOffsetY: 0,\n\n //A property to check whether or not the pointer\n //is visible\n _visible: true,\n get visible() {\n return this._visible;\n },\n set visible(value) {\n if (value === true) {\n this.cursor = \"auto\";\n } else {\n this.cursor = \"none\";\n }\n this._visible = value;\n },\n\n //The pointer's mouse `moveHandler`\n moveHandler(event) {\n\n //Get the element that's firing the event\n let element = event.target;\n\n //Find the pointer’s x and y position (for mouse).\n //Subtract the element's top and left offset from the browser window\n this._x = (event.pageX - element.offsetLeft);\n this._y = (event.pageY - element.offsetTop);\n\n //Prevent the event's default behavior \n event.preventDefault();\n },\n\n //The pointer's `touchmoveHandler`\n touchmoveHandler(event) {\n let element = event.target;\n\n //Find the touch point's x and y position\n this._x = (event.targetTouches[0].pageX - element.offsetLeft);\n this._y = (event.targetTouches[0].pageY - element.offsetTop);\n event.preventDefault();\n },\n\n //The pointer's `downHandler`\n downHandler(event) {\n\n //Set the down states\n this.isDown = true;\n this.isUp = false;\n this.tapped = false;\n\n //Capture the current time\n this.downTime = Date.now();\n\n //Call the `press` method if it's been assigned\n if (this.press) this.press();\n event.preventDefault();\n },\n\n //The pointer's `touchstartHandler`\n touchstartHandler(event) {\n let element = event.target;\n\n //Find the touch point's x and y position\n this._x = event.targetTouches[0].pageX - element.offsetLeft;\n this._y = event.targetTouches[0].pageY - element.offsetTop;\n\n //Set the down states\n this.isDown = true;\n this.isUp = false;\n this.tapped = false;\n\n //Capture the current time\n this.downTime = Date.now();\n\n //Call the `press` method if it's been assigned\n if (this.press) this.press();\n event.preventDefault();\n },\n\n //The pointer's `upHandler`\n upHandler(event) {\n\n //Figure out how much time the pointer has been down\n this.elapsedTime = Math.abs(this.downTime - Date.now());\n\n //If it's less than 200 milliseconds, it must be a tap or click\n if (this.elapsedTime <= 200 && this.tapped === false) {\n this.tapped = true;\n\n //Call the `tap` method if it's been assigned\n if (this.tap) this.tap();\n }\n this.isUp = true;\n this.isDown = false;\n\n //Call the `release` method if it's been assigned\n if (this.release) this.release();\n\n //`event.preventDefault();` needs to be disabled to prevent range sliders\n //from getting trapped in Firefox (and possibly Safari)\n //event.preventDefault();\n },\n\n //The pointer's `touchendHandler`\n touchendHandler(event) {\n\n //Figure out how much time the pointer has been down\n this.elapsedTime = Math.abs(this.downTime - Date.now());\n\n //If it's less than 200 milliseconds, it must be a tap or click\n if (this.elapsedTime <= 200 && this.tapped === false) {\n this.tapped = true;\n\n //Call the `tap` method if it's been assigned\n if (this.tap) this.tap();\n }\n this.isUp = true;\n this.isDown = false;\n\n //Call the `release` method if it's been assigned\n if (this.release) this.release();\n \n //event.preventDefault();\n },\n\n //`hitTestSprite` figures out if the pointer is touching a sprite\n hitTestSprite(sprite) {\n\n //Add global `gx` and `gy` properties to the sprite if they\n //don't already exist\n addGlobalPositionProperties(sprite);\n\n //The `hit` variable will become `true` if the pointer is\n //touching the sprite and remain `false` if it isn't\n let hit = false;\n\n //Find out the sprite's offset from its anchor point\n let xAnchorOffset, yAnchorOffset;\n if (sprite.anchor !== undefined) {\n xAnchorOffset = sprite.width * sprite.anchor.x;\n yAnchorOffset = sprite.height * sprite.anchor.y;\n } else {\n xAnchorOffset = 0;\n yAnchorOffset = 0;\n }\n\n //Is the sprite rectangular?\n if (!sprite.circular) {\n\n //Get the position of the sprite's edges using global\n //coordinates\n let left = sprite.gx - xAnchorOffset,\n right = sprite.gx + sprite.width - xAnchorOffset,\n top = sprite.gy - yAnchorOffset,\n bottom = sprite.gy + sprite.height - yAnchorOffset;\n\n //Find out if the pointer is intersecting the rectangle.\n //`hit` will become `true` if the pointer is inside the\n //sprite's area\n hit = this.x > left && this.x < right && this.y > top && this.y < bottom;\n }\n\n //Is the sprite circular?\n else {\n //Find the distance between the pointer and the\n //center of the circle\n let vx = this.x - (sprite.gx + (sprite.width / 2) - xAnchorOffset),\n vy = this.y - (sprite.gy + (sprite.width / 2) - yAnchorOffset),\n distance = Math.sqrt(vx * vx + vy * vy);\n\n //The pointer is intersecting the circle if the\n //distance is less than the circle's radius\n hit = distance < sprite.width / 2;\n }\n //Check the value of `hit`\n return hit;\n }\n };\n\n //Bind the events to the handlers\n //Mouse events\n element.addEventListener(\n \"mousemove\", pointer.moveHandler.bind(pointer), false\n );\n element.addEventListener(\n \"mousedown\", pointer.downHandler.bind(pointer), false\n );\n\n //Add the `mouseup` event to the `window` to\n //catch a mouse button release outside of the canvas area\n window.addEventListener(\n \"mouseup\", pointer.upHandler.bind(pointer), false\n );\n\n //Touch events\n element.addEventListener(\n \"touchmove\", pointer.touchmoveHandler.bind(pointer), false\n );\n element.addEventListener(\n \"touchstart\", pointer.touchstartHandler.bind(pointer), false\n );\n\n //Add the `touchend` event to the `window` object to\n //catch a mouse button release outside of the canvas area\n window.addEventListener(\n \"touchend\", pointer.touchendHandler.bind(pointer), false\n );\n\n //Disable the default pan and zoom actions on the `canvas`\n element.style.touchAction = \"none\";\n\n //Add the pointer to Tink's global `pointers` array\n this.pointers.push(pointer);\n\n //Return the pointer\n return pointer;\n }\n\n //Many of Tink's objects, like pointers, use collision\n //detection using the sprites' global x and y positions. To make\n //this easier, new `gx` and `gy` properties are added to sprites\n //that reference Pixi sprites' `getGlobalPosition()` values.\n addGlobalPositionProperties(sprite) {\n if (sprite.gx === undefined) {\n Object.defineProperty(\n sprite,\n \"gx\", {\n get() {\n return sprite.getGlobalPosition().x;\n }\n }\n );\n }\n\n if (sprite.gy === undefined) {\n Object.defineProperty(\n sprite,\n \"gy\", {\n get() {\n return sprite.getGlobalPosition().y;\n }\n }\n );\n }\n }\n\n //A method that implments drag-and-drop functionality \n //for each pointer\n updateDragAndDrop(draggableSprites) {\n\n //Create a pointer if one doesn't already exist\n if (this.pointers.length === 0) {\n this.makePointer(this.element, this.scale);\n }\n\n //Loop through all the pointers in Tink's global `pointers` array\n //(there will usually just be one, but you never know)\n this.pointers.forEach(pointer => {\n\n //Check whether the pointer is pressed down\n if (pointer.isDown) {\n\n //You need to capture the co-ordinates at which the pointer was\n //pressed down and find out if it's touching a sprite\n\n //Only run pointer.code if the pointer isn't already dragging\n //sprite\n if (pointer.dragSprite === null) {\n\n //Loop through the `draggableSprites` in reverse to start searching at the bottom of the stack\n for (let i = draggableSprites.length - 1; i > -1; i--) {\n\n //Get a reference to the current sprite\n let sprite = draggableSprites[i];\n\n //Check for a collision with the pointer using `hitTestSprite`\n if (pointer.hitTestSprite(sprite) && sprite.draggable) {\n\n //Calculate the difference between the pointer's\n //position and the sprite's position\n pointer.dragOffsetX = pointer.x - sprite.gx;\n pointer.dragOffsetY = pointer.y - sprite.gy;\n\n //Set the sprite as the pointer's `dragSprite` property\n pointer.dragSprite = sprite;\n\n //The next two lines re-order the `sprites` array so that the\n //selected sprite is displayed above all the others.\n //First, splice the sprite out of its current position in\n //its parent's `children` array\n let children = sprite.parent.children;\n children.splice(children.indexOf(sprite), 1);\n\n //Next, push the `dragSprite` to the end of its `children` array so that it's\n //displayed last, above all the other sprites\n children.push(sprite);\n\n //Reorganize the `draggableSpites` array in the same way\n draggableSprites.splice(draggableSprites.indexOf(sprite), 1);\n draggableSprites.push(sprite);\n\n //Break the loop, because we only need to drag the topmost sprite\n break;\n }\n }\n }\n\n //If the pointer is down and it has a `dragSprite`, make the sprite follow the pointer's\n //position, with the calculated offset\n else {\n pointer.dragSprite.x = pointer.x - pointer.dragOffsetX;\n pointer.dragSprite.y = pointer.y - pointer.dragOffsetY;\n }\n }\n\n //If the pointer is up, drop the `dragSprite` by setting it to `null`\n if (pointer.isUp) {\n pointer.dragSprite = null;\n }\n\n //Change the mouse arrow pointer to a hand if it's over a\n //draggable sprite\n draggableSprites.some(sprite => {\n if (pointer.hitTestSprite(sprite) && sprite.draggable) {\n if (pointer.visible) pointer.cursor = \"pointer\";\n return true;\n } else {\n if (pointer.visible) pointer.cursor = \"auto\";\n return false;\n }\n });\n });\n }\n\n makeInteractive(o) {\n\n //The `press`,`release`, `over`, `out` and `tap` methods. They're `undefined`\n //for now, but they can be defined in the game program\n o.press = o.press || undefined;\n o.release = o.release || undefined;\n o.over = o.over || undefined;\n o.out = o.out || undefined;\n o.tap = o.tap || undefined;\n\n //The `state` property tells you the button's\n //current state. Set its initial state to \"up\"\n o.state = \"up\";\n\n //The `action` property tells you whether its being pressed or\n //released\n o.action = \"\";\n\n //The `pressed` and `hoverOver` Booleans are mainly for internal\n //use in this code to help figure out the correct state.\n //`pressed` is a Boolean that helps track whether or not\n //the sprite has been pressed down\n o.pressed = false;\n\n //`hoverOver` is a Boolean which checks whether the pointer\n //has hovered over the sprite\n o.hoverOver = false;\n\n //tinkType is a string that will be set to \"button\" if the \n //user creates an object using the `button` function\n o.tinkType = \"\";\n\n //Set `enabled` to true to allow for interactivity\n //Set `enabled` to false to disable interactivity\n o.enabled = true;\n\n //Add the sprite to the global `buttons` array so that it can\n //be updated each frame in the `updateButtons method\n this.buttons.push(o);\n }\n\n //The `updateButtons` method will be called each frame \n //inside the game loop. It updates all the button-like sprites\n updateButtons() {\n\n //Create a pointer if one doesn't already exist\n if (this.pointers.length === 0) {\n this.makePointer(this.element, this.scale);\n }\n\n //Loop through all of Tink's pointers (there will usually\n //just be one)\n this.pointers.forEach(pointer => {\n\n pointer.shouldBeHand = false;\n\n\n\n //Loop through all the button-like sprites that were created\n //using the `makeInteractive` method\n this.buttons.forEach(o => {\n\n //Only do this if the interactive object is enabled\n if (o.enabled) {\n\n //Figure out if the pointer is touching the sprite\n let hit = pointer.hitTestSprite(o);\n\n //1. Figure out the current state\n if (pointer.isUp) {\n\n //Up state\n o.state = \"up\";\n\n //Show the first image state frame, if this is a `Button` sprite\n if (o.tinkType === \"button\") o.gotoAndStop(0);\n }\n\n //If the pointer is touching the sprite, figure out\n //if the over or down state should be displayed\n if (hit) {\n\n //Over state\n o.state = \"over\";\n\n //Show the second image state frame if this sprite has\n //3 frames and it's a `Button` sprite\n if (o.totalFrames && o.totalFrames === 3 && o.tinkType === \"button\") {\n o.gotoAndStop(1);\n }\n\n //Down state\n if (pointer.isDown) {\n o.state = \"down\";\n\n //Show the third frame if this sprite is a `Button` sprite and it\n //has only three frames, or show the second frame if it\n //only has two frames\n if (o.tinkType === \"button\") {\n if (o.totalFrames === 3) {\n o.gotoAndStop(2);\n } else {\n o.gotoAndStop(1);\n }\n }\n }\n\n\n\n //Flag this pointer to be changed to a hand\n pointer.shouldBeHand = true;\n //if (pointer.visible) pointer.cursor = \"pointer\";\n // } else {\n // //Turn the pointer to an ordinary arrow icon if the\n // //pointer isn't touching a sprite\n // if (pointer.visible) pointer.cursor = \"auto\";\n\n //Change the pointer icon to a hand\n if (pointer.visible) pointer.cursor = \"pointer\";\n } else {\n //Turn the pointer to an ordinary arrow icon if the\n //pointer isn't touching a sprite\n if (pointer.visible) pointer.cursor = \"auto\";\n\n }\n\n //Perform the correct interactive action\n\n //a. Run the `press` method if the sprite state is \"down\" and\n //the sprite hasn't already been pressed\n if (o.state === \"down\") {\n if (!o.pressed) {\n if (o.press) o.press();\n o.pressed = true;\n o.action = \"pressed\";\n }\n }\n\n //b. Run the `release` method if the sprite state is \"over\" and\n //the sprite has been pressed\n if (o.state === \"over\") {\n if (o.pressed) {\n if (o.release) o.release();\n o.pressed = false;\n o.action = \"released\";\n //If the pointer was tapped and the user assigned a `tap`\n //method, call the `tap` method\n if (pointer.tapped && o.tap) o.tap();\n }\n\n //Run the `over` method if it has been assigned\n if (!o.hoverOver) {\n if (o.over) o.over();\n o.hoverOver = true;\n }\n }\n\n //c. Check whether the pointer has been released outside\n //the sprite's area. If the button state is \"up\" and it's\n //already been pressed, then run the `release` method.\n if (o.state === \"up\") {\n if (o.pressed) {\n if (o.release) o.release();\n o.pressed = false;\n o.action = \"released\";\n }\n\n //Run the `out` method if it has been assigned\n if (o.hoverOver) {\n if (o.out) o.out();\n o.hoverOver = false;\n }\n }\n }\n });\n\n if (pointer.shouldBeHand) {\n pointer.cursor = \"pointer\";\n } else {\n pointer.cursor = \"auto\";\n }\n\n\n });\n }\n\n //A function that creates a sprite with 3 frames that\n //represent the button states: up, over and down\n button(source, x = 0, y = 0) {\n\n //The sprite object that will be returned\n let o;\n\n //Is it an array of frame ids or textures?\n if (typeof source[0] === \"string\") {\n\n //They're strings, but are they pre-existing texture or\n //paths to image files?\n //Check to see if the first element matches a texture in the\n //cache\n if (this.TextureCache[source[0]]) {\n\n //It does, so it's an array of frame ids\n o = this.AnimatedSprite.fromFrames(source);\n } else {\n\n //It's not already in the cache, so let's load it\n o = this.AnimatedSprite.fromImages(source);\n }\n }\n\n //If the `source` isn't an array of strings, check whether\n //it's an array of textures\n else if (source[0] instanceof this.Texture) {\n\n //Yes, it's an array of textures. \n //Use them to make a AnimatedSprite o \n o = new this.AnimatedSprite(source);\n }\n\n //Add interactive properties to the button\n this.makeInteractive(o);\n\n //Set the `tinkType` to \"button\"\n o.tinkType = \"button\";\n\n //Position the button\n o.x = x;\n o.y = y;\n\n //Return the new button sprite\n return o;\n }\n\n //Run the `udpate` function in your game loop\n //to update all of Tink's interactive objects\n update() {\n\n //Update the drag and drop system\n if (this.draggableSprites.length !== 0) this.updateDragAndDrop(this.draggableSprites);\n\n //Update the buttons and button-like interactive sprites\n if (this.buttons.length !== 0) this.updateButtons();\n }\n\n /*\n `keyboard` is a method that listens for and captures keyboard events. It's really\n just a convenient wrapper function for HTML `keyup` and `keydown` events so that you can keep your application code clutter-free and easier to write and read.\n\n Here's how to use the `keyboard` method. Create a new keyboard object like this:\n ```js\n let keyObject = keyboard(asciiKeyCodeNumber);\n ```\n It's one argument is the ASCII key code number of the keyboard key\n that you want to listen for. [Here's a list of ASCII key codes you can\n use](http://www.asciitable.com).\n Then assign `press` and `release` methods to the keyboard object like this:\n ```js\n keyObject.press = () => {\n //key object pressed\n };\n keyObject.release = () => {\n //key object released\n };\n ```\n Keyboard objects also have `isDown` and `isUp` Boolean properties that you can use to check the state of each key. \n */\n keyboard(keyCode) {\n let key = {};\n key.code = keyCode;\n key.isDown = false;\n key.isUp = true;\n key.press = undefined;\n key.release = undefined;\n\n //The `downHandler`\n key.downHandler = event => {\n if (event.keyCode === key.code) {\n if (key.isUp && key.press) key.press();\n key.isDown = true;\n key.isUp = false;\n }\n event.preventDefault();\n };\n\n //The `upHandler`\n key.upHandler = event => {\n if (event.keyCode === key.code) {\n if (key.isDown && key.release) key.release();\n key.isDown = false;\n key.isUp = true;\n }\n event.preventDefault();\n };\n\n //Attach event listeners\n window.addEventListener(\n \"keydown\", key.downHandler.bind(key), false\n );\n window.addEventListener(\n \"keyup\", key.upHandler.bind(key), false\n );\n\n //Return the key object\n return key;\n }\n\n //`arrowControl` is a convenience method for updating a sprite's velocity\n //for 4-way movement using the arrow directional keys. Supply it\n //with the sprite you want to control and the speed per frame, in\n //pixels, that you want to update the sprite's velocity\n arrowControl(sprite, speed) {\n\n if (speed === undefined) {\n throw new Error(\"Please supply the arrowControl method with the speed at which you want the sprite to move\");\n }\n\n let upArrow = this.keyboard(38),\n rightArrow = this.keyboard(39),\n downArrow = this.keyboard(40),\n leftArrow = this.keyboard(37);\n\n //Assign key `press` methods\n leftArrow.press = () => {\n //Change the sprite's velocity when the key is pressed\n sprite.vx = -speed;\n sprite.vy = 0;\n };\n leftArrow.release = () => {\n //If the left arrow has been released, and the right arrow isn't down,\n //and the sprite isn't moving vertically: \n //Stop the sprite\n if (!rightArrow.isDown && sprite.vy === 0) {\n sprite.vx = 0;\n }\n };\n upArrow.press = () => {\n sprite.vy = -speed;\n sprite.vx = 0;\n };\n upArrow.release = () => {\n if (!downArrow.isDown && sprite.vx === 0) {\n sprite.vy = 0;\n }\n };\n rightArrow.press = () => {\n sprite.vx = speed;\n sprite.vy = 0;\n };\n rightArrow.release = () => {\n if (!leftArrow.isDown && sprite.vy === 0) {\n sprite.vx = 0;\n }\n };\n downArrow.press = () => {\n sprite.vy = speed;\n sprite.vx = 0;\n };\n downArrow.release = () => {\n if (!upArrow.isDown && sprite.vx === 0) {\n sprite.vy = 0;\n }\n };\n }\n}"]}
--------------------------------------------------------------------------------