camera-2d-simple
Version:
2D camera for WebGL
260 lines (218 loc) • 8.29 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('gl-matrix')) :
typeof define === 'function' && define.amd ? define(['gl-matrix'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.createCamera2d = factory(global.glMatrix));
}(this, (function (glMatrix) { 'use strict';
var createCamera = function (
initTarget,
initDistance,
initRotation,
initViewCenter,
initScaleBounds,
initTranslationBounds
) {
if ( initTarget === void 0 ) initTarget = [0, 0];
if ( initDistance === void 0 ) initDistance = 1;
if ( initRotation === void 0 ) initRotation = 0;
if ( initViewCenter === void 0 ) initViewCenter = [0, 0];
if ( initScaleBounds === void 0 ) initScaleBounds = [
[0, Infinity],
[0, Infinity] ];
if ( initTranslationBounds === void 0 ) initTranslationBounds = [
[-Infinity, Infinity],
[-Infinity, Infinity] ];
// Scratch variables
var scratch0 = new Float32Array(16);
var scratch1 = new Float32Array(16);
var scratch2 = new Float32Array(16);
var view = glMatrix.mat4.create();
var viewCenter = initViewCenter.slice(0, 2).concat( [0], [1]);
var scaleXBounds = Array.isArray(initScaleBounds[0])
? [].concat( initScaleBounds[0] )
: [].concat( initScaleBounds );
var scaleYBounds = Array.isArray(initScaleBounds[0])
? [].concat( initScaleBounds[1] )
: [].concat( initScaleBounds );
var translationXBounds = Array.isArray(initTranslationBounds[0])
? [].concat( initTranslationBounds[0] )
: [].concat( initTranslationBounds );
var translationYBounds = Array.isArray(initTranslationBounds[0])
? [].concat( initTranslationBounds[1] )
: [].concat( initTranslationBounds );
var getScaling = function () { return glMatrix.mat4.getScaling(scratch0, view).slice(0, 2); };
var getMinScaling = function () {
var scaling = getScaling();
return Math.min(scaling[0], scaling[1]);
};
var getMaxScaling = function () {
var scaling = getScaling();
return Math.max(scaling[0], scaling[1]);
};
var getRotation = function () { return Math.acos(view[0] / getMaxScaling()); };
var getScaleBounds = function () { return [[].concat( scaleXBounds ), [].concat( scaleYBounds )]; };
var getTranslationBounds = function () { return [
[].concat( translationXBounds ),
[].concat( translationYBounds ) ]; };
var getDistance = function () {
var scaling = getScaling();
return [1 / scaling[0], 1 / scaling[1]];
};
var getMinDistance = function () { return 1 / getMinScaling(); };
var getMaxDistance = function () { return 1 / getMaxScaling(); };
var getTranslation = function () { return glMatrix.mat4.getTranslation(scratch0, view).slice(0, 2); };
var getTarget = function () { return glMatrix.vec4
.transformMat4(scratch0, viewCenter, glMatrix.mat4.invert(scratch2, view))
.slice(0, 2); };
var getView = function () { return view; };
var getViewCenter = function () { return viewCenter.slice(0, 2); };
var lookAt = function (ref, newDistance, newRotation) {
if ( ref === void 0 ) ref = [];
var x = ref[0]; if ( x === void 0 ) x = 0;
var y = ref[1]; if ( y === void 0 ) y = 0;
if ( newDistance === void 0 ) newDistance = 1;
if ( newRotation === void 0 ) newRotation = 0;
// Reset the view
view = glMatrix.mat4.create();
translate([-x, -y]);
rotate(newRotation);
scale(1 / newDistance);
};
var translate = function (ref) {
if ( ref === void 0 ) ref = [];
var x = ref[0]; if ( x === void 0 ) x = 0;
var y = ref[1]; if ( y === void 0 ) y = 0;
scratch0[0] = x;
scratch0[1] = y;
scratch0[2] = 0;
var t = glMatrix.mat4.fromTranslation(scratch1, scratch0);
// Translate about the viewport center
// This is identical to `i * t * i * view` where `i` is the identity matrix
glMatrix.mat4.multiply(view, t, view);
};
var scale = function (d, mousePos) {
var isArray = Array.isArray(d);
var dx = isArray ? d[0] : d;
var dy = isArray ? d[1] : d;
if (dx <= 0 || dy <= 0 || (dx === 1 && dy === 1)) { return; }
var scaling = getScaling();
var newXScale = scaling[0] * dx;
var newYScale = scaling[1] * dy;
dx =
Math.max(scaleXBounds[0], Math.min(newXScale, scaleXBounds[1])) /
scaling[0];
dy =
Math.max(scaleYBounds[0], Math.min(newYScale, scaleYBounds[1])) /
scaling[1];
if (dx === 1 && dy === 1) { return; } // There is nothing to do
scratch0[0] = dx;
scratch0[1] = dy;
scratch0[2] = 1;
var s = glMatrix.mat4.fromScaling(scratch1, scratch0);
var scaleCenter = mousePos ? mousePos.concat( [0]) : viewCenter;
var a = glMatrix.mat4.fromTranslation(scratch0, scaleCenter);
// Translate about the scale center
// I.e., the mouse position or the view center
glMatrix.mat4.multiply(
view,
a,
glMatrix.mat4.multiply(
view,
s,
glMatrix.mat4.multiply(view, glMatrix.mat4.invert(scratch2, a), view)
)
);
};
var rotate = function (rad) {
var r = glMatrix.mat4.create();
glMatrix.mat4.fromRotation(r, rad, [0, 0, 1]);
// Rotate about the viewport center
// This is identical to `i * r * i * view` where `i` is the identity matrix
glMatrix.mat4.multiply(view, r, view);
};
var setScaleBounds = function (newBounds) {
var isArray = Array.isArray(newBounds[0]);
scaleXBounds[0] = isArray ? newBounds[0][0] : newBounds[0];
scaleXBounds[1] = isArray ? newBounds[0][1] : newBounds[1];
scaleYBounds[0] = isArray ? newBounds[1][0] : newBounds[0];
scaleYBounds[1] = isArray ? newBounds[1][1] : newBounds[1];
};
var setTranslationBounds = function (newBounds) {
var isArray = Array.isArray(newBounds[0]);
translationXBounds[0] = isArray ? newBounds[0][0] : newBounds[0];
translationXBounds[1] = isArray ? newBounds[0][1] : newBounds[1];
translationYBounds[0] = isArray ? newBounds[1][0] : newBounds[0];
translationYBounds[1] = isArray ? newBounds[1][1] : newBounds[1];
};
var setView = function (newView) {
if (!newView || newView.length < 16) { return; }
view = newView;
};
var setViewCenter = function (newViewCenter) {
viewCenter = newViewCenter.slice(0, 2).concat( [0], [1]);
};
var reset = function () {
lookAt(initTarget, initDistance, initRotation);
};
// Init
lookAt(initTarget, initDistance, initRotation);
return {
get translation() {
return getTranslation();
},
get target() {
return getTarget();
},
get scaling() {
return getScaling();
},
get minScaling() {
return getMinScaling();
},
get maxScaling() {
return getMaxScaling();
},
get scaleBounds() {
return getScaleBounds();
},
get translationBounds() {
return getTranslationBounds();
},
get distance() {
return getDistance();
},
get minDistance() {
return getMinDistance();
},
get maxDistance() {
return getMaxDistance();
},
get rotation() {
return getRotation();
},
get view() {
return getView();
},
get viewCenter() {
return getViewCenter();
},
lookAt: lookAt,
translate: translate,
pan: translate,
rotate: rotate,
scale: scale,
zoom: scale,
reset: reset,
set: function () {
var args = [], len = arguments.length;
while ( len-- ) args[ len ] = arguments[ len ];
console.warn('`set()` is deprecated. Please use `setView()` instead.');
return setView.apply(void 0, args);
},
setScaleBounds: setScaleBounds,
setTranslationBounds: setTranslationBounds,
setView: setView,
setViewCenter: setViewCenter,
};
};
return createCamera;
})));