├── .gitignore
├── phaser.min.js
├── lightworld_large.gif
├── Gruntfile.js
├── package.json
├── scrollable-kinetic.html
├── README.md
├── dist
├── phaser-scrollable.min.js
└── phaser-scrollable.js
└── src
└── phaser-scrollable.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/phaser.min.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trueicecold/phaser-scrollable/HEAD/phaser.min.js
--------------------------------------------------------------------------------
/lightworld_large.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trueicecold/phaser-scrollable/HEAD/lightworld_large.gif
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 |
3 | grunt.initConfig({
4 | pkg: grunt.file.readJSON('package.json'),
5 | concat: {
6 | options: {
7 | separator: ';'
8 | },
9 | dist: {
10 | src: ['src/*.js'],
11 | dest: 'dist/<%= pkg.name %>.js'
12 | }
13 | },
14 | uglify: {
15 | options: {
16 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
17 | },
18 | dist: {
19 | files: {
20 | 'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
21 | }
22 | }
23 | }
24 | });
25 | grunt.loadNpmTasks('grunt-contrib-uglify');
26 | grunt.loadNpmTasks('grunt-contrib-concat');
27 |
28 | grunt.registerTask('default', ['concat','uglify']);
29 |
30 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phaser-scrollable",
3 | "version": "1.0.0",
4 | "description": "A phaser kinetic scrollable group.",
5 | "main": "./dist/phaser-scrollable.min.js",
6 | "dependencies": {
7 | "grunt": "^1.0.1",
8 | "grunt-contrib-concat": "^1.0.1",
9 | "grunt-contrib-uglify": "^3.3.0",
10 | "phaser": "^2.0.6"
11 | },
12 | "devDependencies": {},
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/trueicecold/phaser-scrollable.git"
16 | },
17 | "keywords": [
18 | "phaser",
19 | "scroll",
20 | "scrollable",
21 | "vertical",
22 | "horizontal",
23 | "mousewheel",
24 | "kinetic"
25 | ],
26 | "author": "Yaniv Nagar",
27 | "license": "MIT",
28 | "licenseUrl": "http://www.opensource.org/licenses/mit-license.php",
29 | "bugs": {
30 | "url": "https://github.com/trueicecold/phaser-scrollable/issues"
31 | },
32 | "homepage": "https://github.com/trueicecold/phaser-scrollable#readme"
33 | }
34 |
--------------------------------------------------------------------------------
/scrollable-kinetic.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Phaser scrollable component
2 |
3 | This component can simulate vertical and horizontal scrolling to a Phaser.Group.
4 |
5 | > This component is directly derived from [jdnichollsc](https://github.com/jdnichollsc)'s amazing work on his plugin, [Kinetic Scrolling Plugin](https://github.com/jdnichollsc/Phaser-Kinetic-Scrolling-Plugin). I converted it to use groups instead of moving the camera, added support for multiple scrollers, and some bug fixes.
6 |
7 |
8 | ### Download the Component
9 |
10 | Install using [npm](https://www.npmjs.com/):
11 |
12 | ```bash
13 | npm install https://github.com/trueicecold/phaser-scrollable --save
14 | ```
15 |
16 | ### Using the Component
17 |
18 | ```javascript
19 | this.scroller = game.add.existing(new ScrollableArea(x, y, w, h, params));
20 | var textStyle = {font:"30px Arial", fill:"#ffff00"};
21 | for (var i=0;i<10;i++) {
22 | for (var j=0;j<80;j++) {
23 | var text = game.make.text(i*330, j*30, "Yes, everything scrolls", textStyle);
24 | scroller.addChild(text);
25 | }
26 | }
27 | scroller.start();
28 | ```
29 |
30 | * x - the x position of the scrollable container
31 | * y - the y position of the scrollable container
32 | * w - the width of the scrollable container. If the width of the children inside is larger than this, a horizontal scrolling will be possible.
33 | * h - the height of the scrollable container. If the height of the children inside is larger than this, a vertical scrolling will be possible.
34 | * params - configurable parameters for the component:
35 | * horizontalScroll - allow horizontal scroll. Default is true.
36 | * verticalScroll - allow vertical scroll. Default is true.
37 | * horizontalWheel - allow horizontal wheel support. Default is true. Should not be enabled along with verticalWheel.
38 | * verticalWheel - allow vertical wheel support. Default is false. Should not be enabled along with horizontalWheel.
39 | * kineticMovement - use kinetic scrolling. Default is true. Use false to switch to static scrolling (scrolling stops as you leave the touch).
40 | * timeConstantScroll - The rate of deceleration for the scrolling. Default is 325ms.
41 | * deltaWheel - Delta increment of the mouse wheel.
42 |
43 | ### Change the component parameters at runtime
44 |
45 | ```javascript
46 | scroller.configure(params);
47 | ```
48 |
49 | ### Methods
50 | * start - start the scroller behavior.
51 | * stop - stops the scroller behavior. this does not reset the content position.
52 | * setPosition - object (x,y) - repositions the scroller.
53 | * scrollTo - object (x,y,time,easing,allowScrollStopOnTouch) - scrolls the scroller to the position given.
54 | * time - defaults to 1000
55 | * easing - defaults to Phaser.Easing.Quadratic.Out
56 | * allowScrollStopOnTouch - choose whether to allow the user to cancel the scrolling animation when clicking on the scroller. Defaults to false, useful to cutscenes.
57 |
58 | ### Demo
59 |
60 | [https://cdn.rawgit.com/trueicecold/phaser-scrollable/e2bfe0cd/scrollable-kinetic.html](https://cdn.rawgit.com/trueicecold/phaser-scrollable/e2bfe0cd/scrollable-kinetic.html)
61 |
62 | Made with <3 for
--------------------------------------------------------------------------------
/dist/phaser-scrollable.min.js:
--------------------------------------------------------------------------------
1 | /*! phaser-scrollable 14-01-2018 */
2 |
3 | var ScrollableArea=function(t,i,s,e,h){h=h||{},Phaser.Group.call(this,game),this._x=this.x=t,this._y=this.y=i,this._w=s,this._h=e,this.maskGraphics=game.add.graphics(t,i),this.maskGraphics.beginFill(0),this.maskGraphics.drawRect(0,0,s,e),this.maskGraphics.alpha=.2,this.maskGraphics.inputEnabled=!0,this.mask=this.maskGraphics,this.dragging=!1,this.pressedDown=!1,this.timestamp=0,this.callbackID=0,this.targetX=0,this.targetY=0,this.autoScrollX=!1,this.autoScrollY=!1,this.inputX=0,this.inputY=0,this.startX=0,this.startY=0,this.velocityX=0,this.velocityY=0,this.amplitudeX=0,this.amplitudeY=0,this.directionWheel=0,this.velocityWheelX=0,this.velocityWheelY=0,this.settings={kineticMovement:!0,timeConstantScroll:325,horizontalScroll:!0,verticalScroll:!0,horizontalWheel:!1,verticalWheel:!0,deltaWheel:40,clickXThreshold:5,clickYThreshold:5},this.configure(h),this.addChild=function(t){this.maskGraphics.x=this.parent.x+this._x,this.maskGraphics.y=this.parent.y+this._y,ScrollableArea.prototype.addChild.call(this,t)}};ScrollableArea.prototype=Object.create(Phaser.Group.prototype),ScrollableArea.prototype.constructor=ScrollableArea,ScrollableArea.prototype.configure=function(t){if(t)for(var i in t)this.settings.hasOwnProperty(i)&&null!=this.settings[i]&&(this.settings[i]=t[i])},ScrollableArea.prototype.start=function(){this.game.input.onDown.add(this.beginMove,this),this.callbackID=this.game.input.addMoveCallback(this.moveCanvas,this),this.game.input.onUp.add(this.endMove,this),this.game.input.mouse.mouseWheelCallback=this.mouseWheel.bind(this)},ScrollableArea.prototype.beginMove=function(){this.allowScrollStopOnTouch&&this.scrollTween&&this.scrollTween.pause(),this.maskGraphics.getBounds().contains(this.game.input.x,this.game.input.y)?(this.startedInside=!0,this.startX=this.inputX=this.game.input.x,this.startY=this.inputY=this.game.input.y,this.pressedDown=!0,this.timestamp=Date.now(),this.velocityY=this.amplitudeY=this.velocityX=this.amplitudeX=0):this.startedInside=!1},ScrollableArea.prototype.moveCanvas=function(t,i,s){if(this.pressedDown){this.now=Date.now();var e,h=this.now-this.timestamp;if(this.timestamp=this.now,this.settings.horizontalScroll)0!==(e=i-this.startX)&&(this.dragging=!0),this.startX=i,this.velocityX=1e3*e/(1+h)*.8+.2*this.velocityX,this.x+=e;if(this.settings.verticalScroll)0!==(e=s-this.startY)&&(this.dragging=!0),this.startY=s,this.velocityY=1e3*e/(1+h)*.8+.2*this.velocityY,this.y+=e;this.limitMovement()}},ScrollableArea.prototype.endMove=function(){if(this.startedInside){if(this.pressedDown=!1,this.autoScrollX=!1,this.autoScrollY=!1,!this.settings.kineticMovement)return;if(this.now=Date.now(),this.game.input.activePointer.withinGame&&((this.velocityX>10||this.velocityX<-10)&&(this.amplitudeX=.8*this.velocityX,this.targetX=Math.round(this.x+this.amplitudeX),this.autoScrollX=!0),(this.velocityY>10||this.velocityY<-10)&&(this.amplitudeY=.8*this.velocityY,this.targetY=Math.round(this.y+this.amplitudeY),this.autoScrollY=!0)),this.game.input.activePointer.withinGame||(this.velocityWheelXAbs=Math.abs(this.velocityWheelX),this.velocityWheelYAbs=Math.abs(this.velocityWheelY),this.settings.horizontalScroll&&(this.velocityWheelXAbs<.1||!this.game.input.activePointer.withinGame)&&(this.autoScrollX=!0),this.settings.verticalScroll&&(this.velocityWheelYAbs<.1||!this.game.input.activePointer.withinGame)&&(this.autoScrollY=!0)),Math.abs(game.input.x-this.inputX)<=this.settings.clickXThreshold&&Math.abs(game.input.y-this.inputY)<=this.settings.clickYThreshold)for(var t=0;t0&&this.getChildAt(t).events.onInputUp.dispatch()}},ScrollableArea.prototype.scrollTo=function(t,i,s,e,h){this.scrollTween&&this.scrollTween.pause(),t=t>0?-t:t,i=i>0?-i:i,e=e||Phaser.Easing.Quadratic.Out,s=s||1e3,h=h||!1,this.allowScrollStopOnTouch=h,this.scrollTween=game.add.tween(this),this.scrollTween.to({x:t,y:i},s,e).start()},ScrollableArea.prototype.update=function(){var t;(this.elapsed=Date.now()-this.timestamp,this.velocityWheelXAbs=Math.abs(this.velocityWheelX),this.velocityWheelYAbs=Math.abs(this.velocityWheelY),this.autoScrollX&&0!=this.amplitudeX)&&((t=-this.amplitudeX*Math.exp(-this.elapsed/this.settings.timeConstantScroll))>.5||t<-.5?this.x=this.targetX+t:this.autoScrollX=!1);this.autoScrollY&&0!=this.amplitudeY&&((t=-this.amplitudeY*Math.exp(-this.elapsed/this.settings.timeConstantScroll))>.5||t<-.5?this.y=this.targetY+t:this.autoScrollY=!1);this.autoScrollX||this.autoScrollY||(this.dragging=!1),this.settings.horizontalWheel&&this.velocityWheelXAbs>.1&&(this.dragging=!0,this.amplitudeX=0,this.autoScrollX=!1,this.x+=this.velocityWheelX,this.velocityWheelX*=.95),this.settings.verticalWheel&&this.velocityWheelYAbs>.1&&(this.dragging=!0,this.autoScrollY=!1,this.y+=this.velocityWheelY,this.velocityWheelY*=.95),this.limitMovement()},ScrollableArea.prototype.mouseWheel=function(t){if(this.settings.horizontalWheel||this.settings.verticalWheel){t.preventDefault();var i=120*this.game.input.mouse.wheelDelta/this.settings.deltaWheel;this.directionWheel!=this.game.input.mouse.wheelDelta&&(this.velocityWheelX=0,this.velocityWheelY=0,this.directionWheel=this.game.input.mouse.wheelDelta),this.settings.horizontalWheel&&(this.autoScrollX=!1,this.velocityWheelX+=i),this.settings.verticalWheel&&(this.autoScrollY=!1,this.velocityWheelY+=i)}},ScrollableArea.prototype.stop=function(){this.game.input.onDown.remove(this.beginMove,this),this.callbackID?this.game.input.deleteMoveCallback(this.callbackID):this.game.input.deleteMoveCallback(this.moveCanvas,this),this.game.input.onUp.remove(this.endMove,this),this.game.input.mouse.mouseWheelCallback=null},ScrollableArea.prototype.setPosition=function(t){t.x&&(this.x+=t.x-this._x,this.maskGraphics.x=this._x=t.x),t.y&&(this.y+=t.y-this._y,this.maskGraphics.y=this._y=t.y)},ScrollableArea.prototype.limitMovement=function(){this.settings.horizontalScroll&&(this.x>this._x&&(this.x=this._x),this.x<-(this.width-this._w-this._x)&&(this.width>this._w?this.x=-(this.width-this._w-this._x):this.x=this._x)),this.settings.verticalScroll&&(this.y>this._y&&(this.y=this._y),this.y<-(this.height-this._h-this._y)&&(this.height>this._h?this.y=-(this.height-this._h-this._y):this.y=this._y))};
--------------------------------------------------------------------------------
/src/phaser-scrollable.js:
--------------------------------------------------------------------------------
1 | var ScrollableArea = function(x, y, w, h, params) {
2 | params = params || {};
3 |
4 | Phaser.Group.call(this, game);
5 |
6 | this._x = this.x = x;
7 | this._y = this.y = y;
8 | this._w = w;
9 | this._h = h;
10 |
11 | this.maskGraphics = game.add.graphics(x, y);
12 | this.maskGraphics.beginFill(0x000000);
13 | this.maskGraphics.drawRect(0, 0, w, h);
14 | this.maskGraphics.alpha = 0.2;
15 | this.maskGraphics.inputEnabled = true;
16 | this.mask = this.maskGraphics;
17 |
18 | this.dragging = false;
19 | this.pressedDown = false;
20 | this.timestamp = 0;
21 | this.callbackID = 0;
22 |
23 | this.targetX = 0;
24 | this.targetY = 0;
25 |
26 | this.autoScrollX = false;
27 | this.autoScrollY = false;
28 |
29 | this.inputX = 0;
30 | this.inputY = 0;
31 |
32 | this.startX = 0;
33 | this.startY = 0;
34 |
35 | this.velocityX = 0;
36 | this.velocityY = 0;
37 |
38 | this.amplitudeX = 0;
39 | this.amplitudeY = 0;
40 |
41 | this.directionWheel = 0;
42 |
43 | this.velocityWheelX = 0;
44 | this.velocityWheelY = 0;
45 |
46 | this.settings = {
47 | kineticMovement: true,
48 | timeConstantScroll: 325, //really mimic iOS
49 | horizontalScroll: true,
50 | verticalScroll: true,
51 | horizontalWheel: false,
52 | verticalWheel: true,
53 | deltaWheel: 40,
54 | clickXThreshold: 5,
55 | clickYThreshold: 5,
56 | };
57 |
58 | this.configure(params);
59 |
60 | this.addChild = function(child) {
61 | this.maskGraphics.x = this.parent.x + this._x;
62 | this.maskGraphics.y = this.parent.y + this._y;
63 | ScrollableArea.prototype.addChild.call(this, child);
64 | }
65 | }
66 |
67 | ScrollableArea.prototype = Object.create(Phaser.Group.prototype);
68 | ScrollableArea.prototype.constructor = ScrollableArea;
69 |
70 | /**
71 | * Change Default Settings of the plugin
72 | *
73 | * @method ScrollableArea#configure
74 | * @param {Object} [options] - Object that contain properties to change the behavior of the plugin.
75 | * @param {number} [options.timeConstantScroll=325] - The rate of deceleration for the scrolling.
76 | * @param {boolean} [options.kineticMovement=true] - Enable or Disable the kinematic motion.
77 | * @param {boolean} [options.horizontalScroll=true] - Enable or Disable the horizontal scrolling.
78 | * @param {boolean} [options.verticalScroll=false] - Enable or Disable the vertical scrolling.
79 | * @param {boolean} [options.horizontalWheel=true] - Enable or Disable the horizontal scrolling with mouse wheel.
80 | * @param {boolean} [options.verticalWheel=false] - Enable or Disable the vertical scrolling with mouse wheel.
81 | * @param {number} [options.deltaWheel=40] - Delta increment of the mouse wheel.
82 | */
83 | ScrollableArea.prototype.configure = function (options) {
84 | if (options) {
85 | for (var property in options) {
86 | if (this.settings.hasOwnProperty(property) && this.settings[property] != null) {
87 | this.settings[property] = options[property];
88 | }
89 | }
90 | }
91 | };
92 |
93 | /**
94 | * Start the Plugin.
95 | *
96 | * @method ScrollableArea#start
97 | */
98 | ScrollableArea.prototype.start = function () {
99 | this.game.input.onDown.add(this.beginMove, this);
100 | this.callbackID = this.game.input.addMoveCallback(this.moveCanvas, this);
101 | this.game.input.onUp.add(this.endMove, this);
102 | this.game.input.mouse.mouseWheelCallback = this.mouseWheel.bind(this);
103 | };
104 |
105 | /**
106 | * Event triggered when a pointer is pressed down, resets the value of variables.
107 | */
108 | ScrollableArea.prototype.beginMove = function () {
109 | if (this.allowScrollStopOnTouch && this.scrollTween) {
110 | this.scrollTween.pause();
111 | }
112 |
113 | if (this.maskGraphics.getBounds().contains(this.game.input.x, this.game.input.y)) {
114 | this.startedInside = true;
115 |
116 | this.startX = this.inputX = this.game.input.x;
117 | this.startY = this.inputY = this.game.input.y;
118 | this.pressedDown = true;
119 | this.timestamp = Date.now();
120 | this.velocityY = this.amplitudeY = this.velocityX = this.amplitudeX = 0;
121 | }
122 | else {
123 | this.startedInside = false;
124 | }
125 | };
126 |
127 | /**
128 | * Event triggered when the activePointer receives a DOM move event such as a mousemove or touchmove.
129 | * The camera moves according to the movement of the pointer, calculating the velocity.
130 | */
131 | ScrollableArea.prototype.moveCanvas = function (pointer, x, y) {
132 | if (!this.pressedDown) return;
133 |
134 | this.now = Date.now();
135 | var elapsed = this.now - this.timestamp;
136 | this.timestamp = this.now;
137 |
138 | if (this.settings.horizontalScroll) {
139 | var delta = x - this.startX; //Compute move distance
140 | if (delta !== 0) this.dragging = true;
141 | this.startX = x;
142 | this.velocityX = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityX;
143 | this.x += delta;
144 | }
145 |
146 | if (this.settings.verticalScroll) {
147 | var delta = y - this.startY; //Compute move distance
148 | if (delta !== 0) this.dragging = true;
149 | this.startY = y;
150 | this.velocityY = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityY;
151 | this.y += delta;
152 | }
153 |
154 | this.limitMovement();
155 |
156 | };
157 |
158 | /**
159 | * Event triggered when a pointer is released, calculates the automatic scrolling.
160 | */
161 | ScrollableArea.prototype.endMove = function () {
162 | if (this.startedInside) {
163 | this.pressedDown = false;
164 | this.autoScrollX = false;
165 | this.autoScrollY = false;
166 |
167 | if (!this.settings.kineticMovement) return;
168 |
169 | this.now = Date.now();
170 |
171 | if(this.game.input.activePointer.withinGame) {
172 | if (this.velocityX > 10 || this.velocityX < -10) {
173 | this.amplitudeX = 0.8 * this.velocityX;
174 | this.targetX = Math.round(this.x + this.amplitudeX);
175 | this.autoScrollX = true;
176 | }
177 |
178 | if (this.velocityY > 10 || this.velocityY < -10) {
179 | this.amplitudeY = 0.8 * this.velocityY;
180 | this.targetY = Math.round(this.y + this.amplitudeY);
181 | this.autoScrollY = true;
182 | }
183 | }
184 | if (!this.game.input.activePointer.withinGame) {
185 | this.velocityWheelXAbs = Math.abs(this.velocityWheelX);
186 | this.velocityWheelYAbs = Math.abs(this.velocityWheelY);
187 | if (this.settings.horizontalScroll && (this.velocityWheelXAbs < 0.1 || !this.game.input.activePointer.withinGame)) {
188 | this.autoScrollX = true;
189 | }
190 | if (this.settings.verticalScroll && (this.velocityWheelYAbs < 0.1 || !this.game.input.activePointer.withinGame)) {
191 | this.autoScrollY = true;
192 | }
193 | }
194 |
195 | if (Math.abs(game.input.x-this.inputX) <= this.settings.clickXThreshold && Math.abs(game.input.y-this.inputY) <= this.settings.clickYThreshold) {
196 | for (var i=0;i 0) {
198 | this.getChildAt(i).events.onInputUp.dispatch();
199 | }
200 | }
201 | }
202 | }
203 | };
204 |
205 | ScrollableArea.prototype.scrollTo = function(x, y, time, easing, allowScrollStopOnTouch) {
206 | if (this.scrollTween) {
207 | this.scrollTween.pause();
208 | }
209 |
210 | x = (x > 0) ? -x : x;
211 | y = (y > 0) ? -y : y;
212 | easing = easing || Phaser.Easing.Quadratic.Out;
213 | time = time || 1000;
214 | allowScrollStopOnTouch = allowScrollStopOnTouch || false;
215 |
216 | this.allowScrollStopOnTouch = allowScrollStopOnTouch;
217 | this.scrollTween = game.add.tween(this);
218 | this.scrollTween.to({x:x, y:y}, time, easing).start();
219 | }
220 |
221 | /**
222 | * Event called after all the core subsystems and the State have updated, but before the render.
223 | * Create the deceleration effect.
224 | */
225 | ScrollableArea.prototype.update = function () {
226 |
227 | this.elapsed = Date.now() - this.timestamp;
228 | this.velocityWheelXAbs = Math.abs(this.velocityWheelX);
229 | this.velocityWheelYAbs = Math.abs(this.velocityWheelY);
230 |
231 | if (this.autoScrollX && this.amplitudeX != 0) {
232 | var delta = -this.amplitudeX * Math.exp(-this.elapsed / this.settings.timeConstantScroll);
233 | if (delta > 0.5 || delta < -0.5) {
234 | this.x = this.targetX + delta;
235 | }
236 | else {
237 | this.autoScrollX = false;
238 | //this.x = -this.targetX;
239 | }
240 | }
241 |
242 | if (this.autoScrollY && this.amplitudeY != 0) {
243 |
244 | var delta = -this.amplitudeY * Math.exp(-this.elapsed / this.settings.timeConstantScroll);
245 | if (delta > 0.5 || delta < -0.5) {
246 | this.y = this.targetY + delta;
247 | }
248 | else {
249 | this.autoScrollY = false;
250 | //this.y = -this.targetY;
251 | }
252 | }
253 |
254 | if(!this.autoScrollX && !this.autoScrollY){
255 | this.dragging = false;
256 | }
257 |
258 | if (this.settings.horizontalWheel && this.velocityWheelXAbs > 0.1) {
259 | this.dragging = true;
260 | this.amplitudeX = 0;
261 | this.autoScrollX = false;
262 | this.x += this.velocityWheelX;
263 | this.velocityWheelX *= 0.95;
264 | }
265 |
266 | if (this.settings.verticalWheel && this.velocityWheelYAbs > 0.1) {
267 | this.dragging = true;
268 | this.autoScrollY = false;
269 | this.y += this.velocityWheelY;
270 | this.velocityWheelY *= 0.95;
271 | }
272 |
273 | this.limitMovement();
274 | };
275 |
276 | /**
277 | * Event called when the mousewheel is used, affect the direction of scrolling.
278 | */
279 | ScrollableArea.prototype.mouseWheel = function (event) {
280 | if (!this.settings.horizontalWheel && !this.settings.verticalWheel) return;
281 |
282 | event.preventDefault();
283 |
284 | var delta = this.game.input.mouse.wheelDelta * 120 / this.settings.deltaWheel;
285 |
286 | if (this.directionWheel != this.game.input.mouse.wheelDelta) {
287 | this.velocityWheelX = 0;
288 | this.velocityWheelY = 0;
289 | this.directionWheel = this.game.input.mouse.wheelDelta;
290 | }
291 |
292 | if (this.settings.horizontalWheel) {
293 | this.autoScrollX = false;
294 |
295 | this.velocityWheelX += delta;
296 | }
297 |
298 | if (this.settings.verticalWheel) {
299 | this.autoScrollY = false;
300 |
301 | this.velocityWheelY += delta;
302 | }
303 |
304 | };
305 |
306 | /**
307 | * Stop the Plugin.
308 | *
309 | * @method ScrollableArea#stop
310 | */
311 | ScrollableArea.prototype.stop = function () {
312 | this.game.input.onDown.remove(this.beginMove, this);
313 |
314 | if (this.callbackID) {
315 | this.game.input.deleteMoveCallback(this.callbackID);
316 | }
317 | else {
318 | this.game.input.deleteMoveCallback(this.moveCanvas, this);
319 | }
320 |
321 | this.game.input.onUp.remove(this.endMove, this);
322 |
323 | this.game.input.mouse.mouseWheelCallback = null;
324 |
325 | };
326 |
327 | /**
328 | * Reposition the scroller
329 | */
330 | ScrollableArea.prototype.setPosition = function(position) {
331 | if (position.x) {
332 | this.x += position.x - this._x;
333 | this.maskGraphics.x = this._x = position.x;
334 | }
335 | if (position.y) {
336 | this.y += position.y - this._y;
337 | this.maskGraphics.y = this._y = position.y;
338 | }
339 | }
340 |
341 | /**
342 | * Prevent overscrolling.
343 | */
344 | ScrollableArea.prototype.limitMovement = function() {
345 | if (this.settings.horizontalScroll) {
346 | if (this.x > this._x)
347 | this.x = this._x;
348 | if (this.x < -(this.width-this._w-this._x)) {
349 | if (this.width > this._w) {
350 | this.x = -(this.width-this._w-this._x);
351 | }
352 | else {
353 | this.x = this._x;
354 | }
355 | }
356 | }
357 |
358 | if (this.settings.verticalScroll) {
359 | if (this.y > this._y)
360 | this.y = this._y;
361 | if (this.y < -(this.height-this._h-this._y)) {
362 | if (this.height > this._h) {
363 | this.y = -(this.height-this._h-this._y);
364 | }
365 | else {
366 | this.y = this._y;
367 | }
368 | }
369 | }
370 | }
--------------------------------------------------------------------------------
/dist/phaser-scrollable.js:
--------------------------------------------------------------------------------
1 | var ScrollableArea = function(x, y, w, h, params) {
2 | params = params || {};
3 |
4 | Phaser.Group.call(this, game);
5 |
6 | this._x = this.x = x;
7 | this._y = this.y = y;
8 | this._w = w;
9 | this._h = h;
10 |
11 | this.maskGraphics = game.add.graphics(x, y);
12 | this.maskGraphics.beginFill(0x000000);
13 | this.maskGraphics.drawRect(0, 0, w, h);
14 | this.maskGraphics.alpha = 0.2;
15 | this.maskGraphics.inputEnabled = true;
16 | this.mask = this.maskGraphics;
17 |
18 | this.dragging = false;
19 | this.pressedDown = false;
20 | this.timestamp = 0;
21 | this.callbackID = 0;
22 |
23 | this.targetX = 0;
24 | this.targetY = 0;
25 |
26 | this.autoScrollX = false;
27 | this.autoScrollY = false;
28 |
29 | this.inputX = 0;
30 | this.inputY = 0;
31 |
32 | this.startX = 0;
33 | this.startY = 0;
34 |
35 | this.velocityX = 0;
36 | this.velocityY = 0;
37 |
38 | this.amplitudeX = 0;
39 | this.amplitudeY = 0;
40 |
41 | this.directionWheel = 0;
42 |
43 | this.velocityWheelX = 0;
44 | this.velocityWheelY = 0;
45 |
46 | this.settings = {
47 | kineticMovement: true,
48 | timeConstantScroll: 325, //really mimic iOS
49 | horizontalScroll: true,
50 | verticalScroll: true,
51 | horizontalWheel: false,
52 | verticalWheel: true,
53 | deltaWheel: 40,
54 | clickXThreshold: 5,
55 | clickYThreshold: 5,
56 | };
57 |
58 | this.configure(params);
59 |
60 | this.addChild = function(child) {
61 | this.maskGraphics.x = this.parent.x + this._x;
62 | this.maskGraphics.y = this.parent.y + this._y;
63 | ScrollableArea.prototype.addChild.call(this, child);
64 | }
65 | }
66 |
67 | ScrollableArea.prototype = Object.create(Phaser.Group.prototype);
68 | ScrollableArea.prototype.constructor = ScrollableArea;
69 |
70 | /**
71 | * Change Default Settings of the plugin
72 | *
73 | * @method ScrollableArea#configure
74 | * @param {Object} [options] - Object that contain properties to change the behavior of the plugin.
75 | * @param {number} [options.timeConstantScroll=325] - The rate of deceleration for the scrolling.
76 | * @param {boolean} [options.kineticMovement=true] - Enable or Disable the kinematic motion.
77 | * @param {boolean} [options.horizontalScroll=true] - Enable or Disable the horizontal scrolling.
78 | * @param {boolean} [options.verticalScroll=false] - Enable or Disable the vertical scrolling.
79 | * @param {boolean} [options.horizontalWheel=true] - Enable or Disable the horizontal scrolling with mouse wheel.
80 | * @param {boolean} [options.verticalWheel=false] - Enable or Disable the vertical scrolling with mouse wheel.
81 | * @param {number} [options.deltaWheel=40] - Delta increment of the mouse wheel.
82 | */
83 | ScrollableArea.prototype.configure = function (options) {
84 | if (options) {
85 | for (var property in options) {
86 | if (this.settings.hasOwnProperty(property) && this.settings[property] != null) {
87 | this.settings[property] = options[property];
88 | }
89 | }
90 | }
91 | };
92 |
93 | /**
94 | * Start the Plugin.
95 | *
96 | * @method ScrollableArea#start
97 | */
98 | ScrollableArea.prototype.start = function () {
99 | this.game.input.onDown.add(this.beginMove, this);
100 | this.callbackID = this.game.input.addMoveCallback(this.moveCanvas, this);
101 | this.game.input.onUp.add(this.endMove, this);
102 | this.game.input.mouse.mouseWheelCallback = this.mouseWheel.bind(this);
103 | };
104 |
105 | /**
106 | * Event triggered when a pointer is pressed down, resets the value of variables.
107 | */
108 | ScrollableArea.prototype.beginMove = function () {
109 | if (this.allowScrollStopOnTouch && this.scrollTween) {
110 | this.scrollTween.pause();
111 | }
112 |
113 | if (this.maskGraphics.getBounds().contains(this.game.input.x, this.game.input.y)) {
114 | this.startedInside = true;
115 |
116 | this.startX = this.inputX = this.game.input.x;
117 | this.startY = this.inputY = this.game.input.y;
118 | this.pressedDown = true;
119 | this.timestamp = Date.now();
120 | this.velocityY = this.amplitudeY = this.velocityX = this.amplitudeX = 0;
121 | }
122 | else {
123 | this.startedInside = false;
124 | }
125 | };
126 |
127 | /**
128 | * Event triggered when the activePointer receives a DOM move event such as a mousemove or touchmove.
129 | * The camera moves according to the movement of the pointer, calculating the velocity.
130 | */
131 | ScrollableArea.prototype.moveCanvas = function (pointer, x, y) {
132 | if (!this.pressedDown) return;
133 |
134 | this.now = Date.now();
135 | var elapsed = this.now - this.timestamp;
136 | this.timestamp = this.now;
137 |
138 | if (this.settings.horizontalScroll) {
139 | var delta = x - this.startX; //Compute move distance
140 | if (delta !== 0) this.dragging = true;
141 | this.startX = x;
142 | this.velocityX = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityX;
143 | this.x += delta;
144 | }
145 |
146 | if (this.settings.verticalScroll) {
147 | var delta = y - this.startY; //Compute move distance
148 | if (delta !== 0) this.dragging = true;
149 | this.startY = y;
150 | this.velocityY = 0.8 * (1000 * delta / (1 + elapsed)) + 0.2 * this.velocityY;
151 | this.y += delta;
152 | }
153 |
154 | this.limitMovement();
155 |
156 | };
157 |
158 | /**
159 | * Event triggered when a pointer is released, calculates the automatic scrolling.
160 | */
161 | ScrollableArea.prototype.endMove = function () {
162 | if (this.startedInside) {
163 | this.pressedDown = false;
164 | this.autoScrollX = false;
165 | this.autoScrollY = false;
166 |
167 | if (!this.settings.kineticMovement) return;
168 |
169 | this.now = Date.now();
170 |
171 | if(this.game.input.activePointer.withinGame) {
172 | if (this.velocityX > 10 || this.velocityX < -10) {
173 | this.amplitudeX = 0.8 * this.velocityX;
174 | this.targetX = Math.round(this.x + this.amplitudeX);
175 | this.autoScrollX = true;
176 | }
177 |
178 | if (this.velocityY > 10 || this.velocityY < -10) {
179 | this.amplitudeY = 0.8 * this.velocityY;
180 | this.targetY = Math.round(this.y + this.amplitudeY);
181 | this.autoScrollY = true;
182 | }
183 | }
184 | if (!this.game.input.activePointer.withinGame) {
185 | this.velocityWheelXAbs = Math.abs(this.velocityWheelX);
186 | this.velocityWheelYAbs = Math.abs(this.velocityWheelY);
187 | if (this.settings.horizontalScroll && (this.velocityWheelXAbs < 0.1 || !this.game.input.activePointer.withinGame)) {
188 | this.autoScrollX = true;
189 | }
190 | if (this.settings.verticalScroll && (this.velocityWheelYAbs < 0.1 || !this.game.input.activePointer.withinGame)) {
191 | this.autoScrollY = true;
192 | }
193 | }
194 |
195 | if (Math.abs(game.input.x-this.inputX) <= this.settings.clickXThreshold && Math.abs(game.input.y-this.inputY) <= this.settings.clickYThreshold) {
196 | for (var i=0;i 0) {
198 | this.getChildAt(i).events.onInputUp.dispatch();
199 | }
200 | }
201 | }
202 | }
203 | };
204 |
205 | ScrollableArea.prototype.scrollTo = function(x, y, time, easing, allowScrollStopOnTouch) {
206 | if (this.scrollTween) {
207 | this.scrollTween.pause();
208 | }
209 |
210 | x = (x > 0) ? -x : x;
211 | y = (y > 0) ? -y : y;
212 | easing = easing || Phaser.Easing.Quadratic.Out;
213 | time = time || 1000;
214 | allowScrollStopOnTouch = allowScrollStopOnTouch || false;
215 |
216 | this.allowScrollStopOnTouch = allowScrollStopOnTouch;
217 | this.scrollTween = game.add.tween(this);
218 | this.scrollTween.to({x:x, y:y}, time, easing).start();
219 | }
220 |
221 | /**
222 | * Event called after all the core subsystems and the State have updated, but before the render.
223 | * Create the deceleration effect.
224 | */
225 | ScrollableArea.prototype.update = function () {
226 |
227 | this.elapsed = Date.now() - this.timestamp;
228 | this.velocityWheelXAbs = Math.abs(this.velocityWheelX);
229 | this.velocityWheelYAbs = Math.abs(this.velocityWheelY);
230 |
231 | if (this.autoScrollX && this.amplitudeX != 0) {
232 | var delta = -this.amplitudeX * Math.exp(-this.elapsed / this.settings.timeConstantScroll);
233 | if (delta > 0.5 || delta < -0.5) {
234 | this.x = this.targetX + delta;
235 | }
236 | else {
237 | this.autoScrollX = false;
238 | //this.x = -this.targetX;
239 | }
240 | }
241 |
242 | if (this.autoScrollY && this.amplitudeY != 0) {
243 |
244 | var delta = -this.amplitudeY * Math.exp(-this.elapsed / this.settings.timeConstantScroll);
245 | if (delta > 0.5 || delta < -0.5) {
246 | this.y = this.targetY + delta;
247 | }
248 | else {
249 | this.autoScrollY = false;
250 | //this.y = -this.targetY;
251 | }
252 | }
253 |
254 | if(!this.autoScrollX && !this.autoScrollY){
255 | this.dragging = false;
256 | }
257 |
258 | if (this.settings.horizontalWheel && this.velocityWheelXAbs > 0.1) {
259 | this.dragging = true;
260 | this.amplitudeX = 0;
261 | this.autoScrollX = false;
262 | this.x += this.velocityWheelX;
263 | this.velocityWheelX *= 0.95;
264 | }
265 |
266 | if (this.settings.verticalWheel && this.velocityWheelYAbs > 0.1) {
267 | this.dragging = true;
268 | this.autoScrollY = false;
269 | this.y += this.velocityWheelY;
270 | this.velocityWheelY *= 0.95;
271 | }
272 |
273 | this.limitMovement();
274 | };
275 |
276 | /**
277 | * Event called when the mousewheel is used, affect the direction of scrolling.
278 | */
279 | ScrollableArea.prototype.mouseWheel = function (event) {
280 | if (!this.settings.horizontalWheel && !this.settings.verticalWheel) return;
281 |
282 | event.preventDefault();
283 |
284 | var delta = this.game.input.mouse.wheelDelta * 120 / this.settings.deltaWheel;
285 |
286 | if (this.directionWheel != this.game.input.mouse.wheelDelta) {
287 | this.velocityWheelX = 0;
288 | this.velocityWheelY = 0;
289 | this.directionWheel = this.game.input.mouse.wheelDelta;
290 | }
291 |
292 | if (this.settings.horizontalWheel) {
293 | this.autoScrollX = false;
294 |
295 | this.velocityWheelX += delta;
296 | }
297 |
298 | if (this.settings.verticalWheel) {
299 | this.autoScrollY = false;
300 |
301 | this.velocityWheelY += delta;
302 | }
303 |
304 | };
305 |
306 | /**
307 | * Stop the Plugin.
308 | *
309 | * @method ScrollableArea#stop
310 | */
311 | ScrollableArea.prototype.stop = function () {
312 | this.game.input.onDown.remove(this.beginMove, this);
313 |
314 | if (this.callbackID) {
315 | this.game.input.deleteMoveCallback(this.callbackID);
316 | }
317 | else {
318 | this.game.input.deleteMoveCallback(this.moveCanvas, this);
319 | }
320 |
321 | this.game.input.onUp.remove(this.endMove, this);
322 |
323 | this.game.input.mouse.mouseWheelCallback = null;
324 |
325 | };
326 |
327 | /**
328 | * Reposition the scroller
329 | */
330 | ScrollableArea.prototype.setPosition = function(position) {
331 | if (position.x) {
332 | this.x += position.x - this._x;
333 | this.maskGraphics.x = this._x = position.x;
334 | }
335 | if (position.y) {
336 | this.y += position.y - this._y;
337 | this.maskGraphics.y = this._y = position.y;
338 | }
339 | }
340 |
341 | /**
342 | * Prevent overscrolling.
343 | */
344 | ScrollableArea.prototype.limitMovement = function() {
345 | if (this.settings.horizontalScroll) {
346 | if (this.x > this._x)
347 | this.x = this._x;
348 | if (this.x < -(this.width-this._w-this._x)) {
349 | if (this.width > this._w) {
350 | this.x = -(this.width-this._w-this._x);
351 | }
352 | else {
353 | this.x = this._x;
354 | }
355 | }
356 | }
357 |
358 | if (this.settings.verticalScroll) {
359 | if (this.y > this._y)
360 | this.y = this._y;
361 | if (this.y < -(this.height-this._h-this._y)) {
362 | if (this.height > this._h) {
363 | this.y = -(this.height-this._h-this._y);
364 | }
365 | else {
366 | this.y = this._y;
367 | }
368 | }
369 | }
370 | }
--------------------------------------------------------------------------------