GithubHelp home page GithubHelp logo

davidfig / pixi-viewport Goto Github PK

View Code? Open in Web Editor NEW
994.0 13.0 165.0 45.86 MB

A highly configurable viewport/2D camera designed to work with pixi.js

Home Page: https://davidfig.github.io/pixi-viewport/

License: MIT License

JavaScript 11.08% TypeScript 88.89% Shell 0.04%
viewport pixi 2d-camera touch zoom bounce

pixi-viewport's Introduction

pixi-viewport

A highly configurable viewport/2D camera designed to work with pixi.js.

Features include dragging, pinch-to-zoom, mouse wheel zooming, decelerated dragging, follow target, animate, snap to point, snap to zoom, clamping, bouncing on edges, and move on mouse edges. See the live examples below to try out all of these features.

All features are configurable and removable, so set up the viewport to be exactly what you need.

Support pixi-viewport!

With your support, I can make pixi-viewport even better! Please consider making a donation:

v5.2+

Moves pixi-viewport to pixi.js v7.2+

NOTE: there is a breaking change since in pixi.js v7.2 interactive (boolean) was deprecated in favor of eventMode.

v5+

Moves pixi-viewport to pixi.js v7 (thanks @cuire!).

NOTE: there is a breaking change since pixi-viewport moved to pixi's new event system. options.interaction is removed and you need pass options.events to the viewport for it to work properly. The events object can be found at pixi's renderer.events or app.renderer.events.

const viewport = new Viewport({ events: renderer.events });

// or
// const viewport = new Viewport({ events: app.renderer.events });

v4.30.0+

This project was migrated to Typescript (thanks @ShukantPal!). All functionality should be the same. The live Example has been updated.

Live Examples

API Documentation

https://davidfig.github.io/pixi-viewport/jsdoc/

Simple Example

import * as PIXI from 'pixi.js'
import { Viewport } from 'pixi-viewport'

// or with require
// const PIXI = require('pixi.js')
// const Viewport = require('pixi-viewport').Viewport

const app = new PIXI.Application()
document.body.appendChild(app.view)

// create viewport
const viewport = new Viewport({
    screenWidth: window.innerWidth,
    screenHeight: window.innerHeight,
    worldWidth: 1000,
    worldHeight: 1000,

    events: app.renderer.events // the interaction module is important for wheel to work properly when renderer.view is placed or scaled
})

// add the viewport to the stage
app.stage.addChild(viewport)

// activate plugins
viewport
    .drag()
    .pinch()
    .wheel()
    .decelerate()

// add a red box
const sprite = viewport.addChild(new PIXI.Sprite(PIXI.Texture.WHITE))
sprite.tint = 0xff0000
sprite.width = sprite.height = 100
sprite.position.set(100, 100)

Installation

yarn add pixi-viewport

or

npm i pixi-viewport

or grab the latest release and use it:

<script src="/directory-to-file/pixi.js"></script>
<script src="/directory-to-file/viewport.min.js"></script>
<!-- or <script type="module" src="/directory-to-file/esm/viewport.es.js"></script> -->
<script>
    const Viewport = new pixi_viewport.Viewport(options)
</script>

Migration from pixi-viewport v3 to v4

Viewport needs to be imported or required as follows:

import { Viewport } from 'pixi-viewport'

// or

const Viewport = require('pixi-viewport').Viewport

Plugins have been moved to their own object:

// viewport.pausePlugin('drag')
viewport.plugins.pause('drag')

// viewport.resumePlugin('drag')
viewport.plugins.resume('drag')

// viewport.removePlugin('drag')
viewport.plugins.remove('drag')

// viewport.userPlugin('name', plugin, index)
viewport.plugins.add('name', plugin, index)

Tests

  1. Clone repository
  2. yarn install
  3. yarn test (for Mocha test code)
  4. yarn coverage (for Instanbul coverage)

Development Recipe

  1. clone repository
  2. yarn install
  3. yarn dev
  4. open browser to http://localhost:5173

PRs are more than welcome!

Other Libraries

If you liked pixi-viewport, please try my other open source libraries:

  • pixi-scrollbox - pixi.js scrollbox: a masked box that can scroll vertically or horizontally with scrollbars (uses pixi-viewport)
  • pixi-ease - pixi.js animation library using easing functions
  • intersects - a simple collection of 2d collision/intersects functions. Supports points, circles, lines, axis-aligned boxes, and polygons

license

MIT License (c) 2023 YOPEY YOPEY LLC by David Figatner ([email protected])

pixi-viewport's People

Contributors

aeolun avatar blnvdanil avatar cuire avatar davidfig avatar defaultsamson avatar ertrzyiks avatar fbessou avatar frankiepo avatar izumi-kun avatar kaflake avatar lidicomgames avatar littleboarx avatar macropp avatar mcdenhoed avatar mhazy avatar mrbuchmann avatar mszu avatar nikolas avatar richard-ejem avatar rjwadley avatar schekle avatar shukantpal avatar slaesh avatar sransara avatar stevensnoeijen avatar superschek avatar svetlanaio avatar uahic avatar viktor286 avatar willyuum avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pixi-viewport's Issues

Test more-pixi branch

I did some refactoring over the last few days and I wanted feedback/testing before merging with master. The changes are in the more-pixi branch.

There is one breaking change, and a bunch of under-the-hood changes:

  1. Viewport is now a child of PIXI.Container. This means you no longer pass a container, but viewport is the container. This cleaned up some of the code. (I originally had it separate to avoid a peer dependency, but decided this was more robust.)

  2. I removed the yy-loop and yy-input dependencies and now fully rely on PIXI.ticker and PIXI.interaction for all looping and interaction. This means viewport no longer ships with its own start/stop functionality. This had the added benefit of a significant perf improvement. Not sure if yy-loop was the culprit (I spent many hours trying to figure out what was going on). Either way, this is cleaner and more in line with a true PIXI library.

  3. hitArea makes a return to set the hit area for the viewport. The hitArea defaults to the passed world size. You can also statically change it by setting options/viewport.forceHitArea

Create feature demos

The current demo is good for testing, but the gui is way too confusing for anyone just trying out pixi-viewport for the first time. I was thinking of creating multiple demos that show off the plugins:

  • current set of plugins from the demo
  • just drag() and pinch()
  • follow()
  • mouseEdges()

I also want to make the demos prettier. I'm tired of the square stars :)

Error performing a snap-zoom and snap simultaneously

When snap-zoom is running and options.center = false for snap the viewport misbehaves because it requires an Ease.to(...) object to have constantly updating inputs in order for snap to properly calculate the top left corner (this is because snap-zoom is constantly changing the scale of the viewport).

A possible but hacky solution is to "look ahead" to the final values of the snap-zoom plugin and use that x and y scale when calculating the targetX and targetY inputs of the Ease.to(...) object in the snap function.

Animation when using zoomPercent()

Hai David thank you for your work.
Is there a way to make zoom become an animation instead of direct zoom?
For example when i use : zoomPercent(2,2)

Bug with drag smoothing

When you drag and release, the viewport is designed to have an effect where it gradually comes to a stop depending on the velocity of which you released it.

I found that after zooming in and out, the effect quickens and seemingly "teleports" the viewport to the end position rather than gradually coming to a stop there.

Here's a video that shows what I mean
https://youtu.be/JqgNEMeLg4Y

This is the code that I'm currently using to invoke the zoom

const maxHeight = 1000;
const minHeight = 100;
const zoomRate = 40 / 100;

function updateMouse(dx, dy, dz, ev) {
    var currentHeight = viewport.bottom - viewport.top;
    var newHeight = currentHeight + (zoomRate * dy);
    viewport.fitHeight(Math.min(Math.max(newHeight, minHeight), maxHeight));
    viewport.update();
}
require('mouse-wheel')(updateMouse, true);

And here's how I instantiate the viewport

var options = {
    screenWidth: w * 2,
    screenHeight: h,
    worldWidth: w,
    worldHeight: h,
};

var viewport = new Viewport(game.stage, options);

viewport
    .drag()
    .hitArea()
    .decelerate()
    .start();

Thanks!

viewport doesn't work properly when parent is scaled

When the viewport's parent scale is not 1, the input distance calculations are off.

For example, my current game uses a viewport within a scaled region. The drags are not scaled down to account for the parent's scale.

Wheel({center: worldEdge }) edge case

If you have wheel on center, and your object is at 0px (and the edge of the world is at 0px). Then zooming in zooms you into 0px, as opposed to screenwidth/2.

I would love to submit a PR to accomplish them but I may need some guidance.

snap / snapZoom waits until center / width / height change before start

Hi, i mentioned that if the center before snap is equal with center after snap, snap plugin will wait for center change before start, same behaviour with snapZoom plugin - if width for example before and after snapZoom is viewport.screenWidth / 3 plugin will wait for width change before start.

Is this expected behaviour and i should check for center and width before calling these plugins ?

clampZoomPercent?

Hi, I have a suggestion for what I would guess is a common use case.

ClampZoom lets you clamp relative to the size of the viewport, but I'm interested in clamping relative to the container's scale so that the min and max zoom are always values that scale nicely, e.g. x0.5 to x2. Something along the lines of: new Viewport({...}).pinch().clampZoom({min: 0.5, max: 2});. Allowing the same with snapZoom would be nice as well.

Maybe there's already a straightforward way of doing this with the current API that I'm missing.

Drag stops when finger cross browser search bar, bottom toolbar.

I have basic setup with pinch, drag, bounce, clampZoom and decelerate plugins.

Everything works fine, until the moment when i drag the stage and drag-end triggered on mobile browser search bar or bottom toolbar (need exactly release the finger) - stage just stuck. Drag completely stops, but pinch still works.

Tested this on IOS Safari / Chrome and Android Chrome browsers.

wheel() is reversed by default

A nitty-gritty improvement, by default the .wheel() plugin will zoom out when scrolling up and zoom in when scrolling down (which is the opposite of what most scroll implementations do).

Easy fix is to do

.wheel({percent: -0.1})

but it would be nice to have this by default.

Restrict viewport to the world borders when follows object

When using follow method - viewport shows areas beyond world box. I restrict viewport position by next lines inside the update method of the follow plugin:

this.parent.x = Math.min(this.parent.x, 0);
this.parent.y = Math.min(this.parent.y, 0);
this.parent.x = Math.max(this.parent.x, this.parent.screenWidth - this.parent.worldWidth);
this.parent.y = Math.max(this.parent.y, this.parent.screenHeight - this.parent.worldHeight);

Is there a better way to do this and maybe this should be done on a higher level?

Zoom pointer position offset after resizing the canvas

Hi,

It seems that the zoom pointer position becomes off after auto resizing the canvas.

This happens when:

  • setting app.renderer.autoresize = true;
  • resizing the canvas (by opening the developer tools on the right side for example)

Here is what it looks like:

  1. Before resizing the canvas, I aim at the green square and the pink one, which works fine
  2. After resizing the canvas, aiming at the yellow square and the purple one becomes "off" (have to recenter the viewport)

pixi_viewport

Here is the code:

var pixi_container = $('#pixi');
var w = pixi_container.innerWidth();
var h = $(document).innerHeight() - pixi_container.offset().top;

var [W, H] = [4 * 1024, 3 * 1024];

// create viewport
var viewport = new Viewport({
  screenWidth: w,
  screenHeight: h,
  worldWidth: W,
  worldHeight: H,
});

// add the viewport to the stage
var app = new PIXI.Application({
  width: width,
  height: height,
  antialias: true,
  transparent: false,
  resolution: 1,
  backgroundColor: 0x00aabb
});
app.renderer.autoResize = true;
app.stage.addChild(viewport);
container.appendChild(app.view);

// activate plugins
viewport
  .drag()
  .clamp()
  .pinch({ noDrag: true })
  .wheel()
  .clampZoom({ maxWidth: W, maxHeight: H, minWidth: w, minHeight: h })
  .decelerate();

viewport.fit().resize(w, h, W, H);
viewport.moveCenter(W / 2, H / 2);  // center viewport

// add a bunch of red boxes
for (let i = 0; i < 100; i++) {
  var sprite = viewport.addChild(new PIXI.Sprite(PIXI.Texture.WHITE));
  sprite.anchor.set(0.5);
  sprite.tint = 0xff0000 * Math.random();
  sprite.width = sprite.height = Math.random() * 200;
  sprite.position.set(Math.random() * W, Math.random() * H);
}

The reason for this might be related to the autoResize flag on the Pixi renderer being set as a CSS property, and the mouse coordinates not taking that into account.

What would be the simplest way to solve this?

Thanks for the help!

Camera Boundary

I am trying to figure out how to create a camera boundary, so it stops at a certain x and y value. Is this built in?

: )

es5!

I was going to create a fork that transpiles to es5 since that was giving me issues with create react app. I figured I might end up submitting a PR for it (since it seems fairly benign and simple) so I might as well ask first.

I tried using browserify to accomplish this but I was having issues converting it to es5 on the fly, in the index file. I was easily able to do it on the command line (to publish it to docs). But that isn't the entry point on npm.

So I will likely use babel for it.

I don't mind trying to do this in different ways. : :)

If this sounds useful I will submit a PR.

Viewport hitArea is affected by viewport transforms

When the viewport is moved or dragged, the underlying hitArea for firing events moves with it.

E.g. I have a viewport with a box drawn with the top-left corner at 0, 0. Currently, I can click anywhere and the mouse click will fire the desired events.
1
Then I drag my viewport to the top and to the left...
2
Now I can only trigger events by clicking this area (highlighted in blue)
3

In addition to this

scaling of the viewport has the same effect. Zooming out makes the hitArea smaller on the user's screen and zooming in makes it larger

Wheel behavior

Hello Figatner, I noticed that the zoom calculation in the wheel event is calculated based in the current container scale value.

For example, let's say that the wheel event was initialized with a 2% value. If you zoom in a single time, the scale value will go to 1.02. But if you zoom out after it, it won't go back to 1, it will go back to 0.9996. Is this the expected behavior?

Class 'Viewport' incorrectly extends base class 'Container'

Hi David,
I am getting following error with viewport. Your help is required.
node_modules/pixi-viewport/@types/index.d.ts (176,15): Class 'Viewport' incorrectly extends base class 'Container'.
Types of property 'on' are incompatible.
Type '{ (event: ViewportEventType, fn: (viewport: Viewport) => void, context?: any): this; (event: View...' is not assignable to type '{ (event:
"added" | "removed", fn: (displayObject: DisplayObject) => void, context?: any): this; ...'.
Types of parameters 'event' and 'event' are incompatible.
Type '"added" | "removed"' is not assignable to type 'ViewportEventType'.
Type '"added"' is not assignable to type 'ViewportEventType'.

Improvement: Add an ease option to the follow plugin

When a user clicks on an object it'd be nice to choose an easing. For example, a sinusoidal easing so that if the object is far from the center it will quickly move and slow down as it starts to approach the center the object.

This may present some difficulty, as contrary to the snap plugin, the follow plugin will need to account for moving object where the desired target coordinates are constantly changing.

Strange transformations upon pinching

The bug happens when the viewport is zoomed out past the max width/height or zoomed in past the min width/height by pinching (using the pinch plugin)

Here's a live demo of the bug that you can try on a touchscreen of your own
https://www.student.cs.uwaterloo.ca/~smclose/bunny-pinch-error/
By default the viewport is set to its max height boundary, so zooming in works (until you hit the minHeight), and zooming out immediately causes the transformation bug.

Here's a video that showcases the bug: https://youtu.be/ims7iuUJwag

Here is the code that the website is running

require('pixi.js');
const Viewport = require('pixi-viewport');

const maxHeight = 1000;
const minHeight = 100;

var h = 600;
var w = 600;

// Creates the PIXI application
var game = new PIXI.Application(w, h, {
    antialias: false,
    transparent: false,
    resolution: 1
});

// Sets up the element
game.view.style.position = "absolute";
game.view.style.display = "block";
document.body.appendChild(game.view);
game.renderer.autoResize = true;
game.renderer.backgroundColor = 0x00FFFF;

var options = {
    screenWidth: w,
    screenHeight: h,
    worldWidth: w,
    worldHeight: h,
};

var viewport = new Viewport(game.stage, options);

var scrollOptions = {
    noDrag: false,
    minWidth: 10,
    minHeight: minHeight,
    maxWidth: 1000000,
    maxHeight: maxHeight,
}

viewport
    .drag()
    .hitArea()
    .pinch(scrollOptions)
    .decelerate()
    .start();

// This creates a texture from a 'bunny.png' image.
PIXI.loader.add('bunny', 'game/bunny.png').load(function (loader, resources) {
    var bunny = new PIXI.Sprite(resources.bunny.texture);
    bunny.x = game.renderer.width / 2;
    bunny.y = game.renderer.height / 2;
    bunny.anchor.x = 0.5;
    bunny.anchor.y = 0.5;
    game.stage.addChild(bunny);
});

Cannot find namespace 'PIXI'

Hi
I am getting 'Cannot find namespace 'PIXI'' error in index.d.ts file. Any workaround is available to this?

I am using it in Angular application.

versions:
"pixi-viewport": "^2.7.2",
"pixi.js": "^4.8.1",

clicking becomes impossible after dragging outside the screen

I'm activating the viewport with drag(), pinch(), bounce(), wheel() and decelerate().

I start to drag normally, but make the drop outside the browser area (tried on iPhone Safari, but the same happens on Chrome when simulating a device).

From that point on, the viewport is neither clickable nor draggable (but strangely remains pinch-able), and the page has to be refreshed by the user.

I'm using version [email protected]

All kinds of moved should be separated

All the moved should not be mixed together
drag-end
drag-start
There should be one ๏ผš drag-moved
The drag process should also have an independent event

Typescript definition file

Hi David,

First of all, great little library with a lot of handy features! well done!

I'm only missing a definitions file for Typescript projects, where I have to import * as Viewport from "pixi-viewport"; and put a // @ts-ignore on top of it so that it doesn't complain about Viewport having any type.

I'm sure many others will find @types/pixi-viewport npm package useful. I don't have too much experience with publishing packages on npm, especially for types, but I'm willing to help.

Keep up the great work!
Gio

Setting options.pauseOnBlur has no effect

Upon constructing the viewport, the option options.pauseOnBlur doesn't seem to do anything. Especially noticable with the follow, decelerate, and snap plugins. When switching to a different tab, I expect the plugins to still be constantly running when I set options.pauseOnBlur to false. However, the actual result is that pixi-viewport plugins halt until I switch back to the original tab.

Viewport coordinates do not exist, viewport can't follow

I can't tell if I'm using the plugin incorrectly, or if there's a bug happening.

I'm using pixi-viewport 0.6.1.

  1. When I try to get the center coordinates of a viewport, it returns {x: NaN, y: NaN}
  2. When I try to follow a target the camera will stay in one spot and prevent me from dragging it.

Here's the general outline of what I'm doing, does anything significantly stand out?

var game = new PIXI.Application(600, 600, {
    antialias: false,
    transparent: false,
    resolution: 1
});

const Viewport = require('pixi-viewport');

var viewport = new Viewport(game.stage);
viewport
    .drag()
    .pinch()
    .hitArea()
    .decelerate()
    .bounce()
    .start();

var bunny = new PIXI.Sprite(resources.bunny.texture);

bunny.x = game.renderer.width / 2;
bunny.y = game.renderer.height / 2;

var target = game.stage.addChild(bunny);

game.ticker.add(function () {
    bunny.x += 5

    console.log('x: ' + viewport.center.x + ' y: ' + viewport.center.y)
});

viewport.follow(bunny);

Freezing issues with mouse wheel

With the latest npm build, I'm experiencing freezing of the viewport when trying to zoom with the mouse wheel. Probably related to the options.divWheel change.

How to set mouse event offset?

I encountered this issue with pixi-scrollbox and yy-ui. Whenever the WebGL Canvas element which contains PIXI does not start from 0,0 x/y in the browser window, the mouse events will not correctly offset. For example if I try to scroll down with the mouse wheel, then mouse events will trigger if my mouse is over 0,0, which is not where the canvas is. Say the canvas is placed at 100,100 and is 300 in width and 300 in height. Well the mouse event will trigger at 0,0 where the canvas isn't even at, but it will not trigger at 400,400 where the canvas is, because it will be offset by it's browser window position.

It seems to understand where the PIXI.JS container/display object is relative to the start of the browser window x/y position, but NOT where the canvas actually is, resulting in all mouse events triggering only outside of the actual canvas if the canvas does not start perfectly at 0,0 in the browser.

Why is the mouse position calculated by where the mouse at relative to the browser window and not relative to where the canvas is placed / inside the canvas, and how can I fix that / correctly offset it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.