Google Street View with deviceorientation (Full Tilt) - google-maps

I am trying to create a Google Street View that can be controlled via a devices orientation.
I am using Full Tilt to normalize devices / OS and it seems to be doing a really good job. In fact, it seems very close indeed to the solution I'm after.
However, at various positions, the view seems to jump rapidly (although it seems to resolve to the correct location) and is not as smooth as I would like. This seems particularly obvious if the device is held at around 90 degrees (upright) and rotated slightly to the left or right.
JS Fiddle has an example and would need to be viewed on a mobile device or tablet to see the effect. Following the link further down will only work if you can get to the edit mode where the deviceorientation is allowed to work.
Any ideas if there is a way to resolve this? Or if I'm on to a loser here?
var map;
google.maps.event.addDomListener(window, "load", function () {
// Set up map
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 9,
// Initial
center:{
lat: 35.6735408,
lng: 139.570305,
},
disableDefaultUI: true,
streetViewControl: true,
});
// Set up street view
google.maps.streetViewViewer = 'photosphere';
var street_view = new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
addressControl: false,
fullscreenControl: false,
zoomControl: false,
});
street_view.setPano('2qVB_HA94-kQCAzwffO44g');
// When street view ready, update map position and pov
street_view.addListener('status_changed', function(){
var position = street_view.getPosition();
if( map ){
map.setCenter(position);
map.setStreetView(street_view);
}
});
// FULL TILT ORIENTATION
var promise = new FULLTILT.getDeviceOrientation({ 'type': 'world' });
var deviceOrientation;
promise
.then(function(controller) {
deviceOrientation = controller;
})
.catch(function(message) {
console.error(message);
});
(function draw() {
if (deviceOrientation) {
var euler = deviceOrientation.getScreenAdjustedEuler();
// debug
console.log('alpha: %d, beta: %d, gamma: %d', euler.alpha, euler.beta, euler.gamma);
document.getElementById('alpha').innerHTML = euler.alpha.toFixed(2);
document.getElementById('beta').innerHTML = euler.beta.toFixed(2);
document.getElementById('gamma').innerHTML = euler.gamma.toFixed(2);
// Set POV
if( euler.alpha != 0 && euler.beta != 0 && euler.gamma !=0 ){
street_view.setPov({
heading: euler.alpha*-1,
pitch: euler.beta-90,
});
}
}
requestAnimationFrame(draw);
})();
});
/*! Full Tilt v0.5.2 / http://github.com/richtr/Full-Tilt */
!function(a){function b(a){return a=+a,0===a||isNaN(a)?a:a>0?1:-1}function c(a){var b=new Promise(function(b,c){var d=function(e){setTimeout(function(){a&&a.data?b():e>=20?c():d(++e)},50)};d(0)});return b}function d(){o=n?(a.screen.orientation.angle||0)*j:(a.orientation||0)*j}function e(a){l.orientation.data=a;for(var b in l.orientation.callbacks)l.orientation.callbacks[b].call(this)}function f(a){l.motion.data=a;for(var b in l.motion.callbacks)l.motion.callbacks[b].call(this)}if(void 0===a.FULLTILT||null===a.FULLTILT){var g=Math.PI,h=g/2,i=2*g,j=g/180,k=180/g,l={orientation:{active:!1,callbacks:[],data:void 0},motion:{active:!1,callbacks:[],data:void 0}},m=!1,n=a.screen&&a.screen.orientation&&void 0!==a.screen.orientation.angle&&null!==a.screen.orientation.angle?!0:!1,o=(n?a.screen.orientation.angle:a.orientation||0)*j,p=h,q=g,r=i/3,s=-h,t={};t.version="0.5.2",t.getDeviceOrientation=function(a){var b=new Promise(function(b,d){var e=new t.DeviceOrientation(a);e.start();var f=new c(l.orientation);f.then(function(){e._alphaAvailable=l.orientation.data.alpha&&null!==l.orientation.data.alpha,e._betaAvailable=l.orientation.data.beta&&null!==l.orientation.data.beta,e._gammaAvailable=l.orientation.data.gamma&&null!==l.orientation.data.gamma,b(e)}).catch(function(){e.stop(),d("DeviceOrientation is not supported")})});return b},t.getDeviceMotion=function(a){var b=new Promise(function(b,d){var e=new t.DeviceMotion(a);e.start();var f=new c(l.motion);f.then(function(){e._accelerationXAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.x,e._accelerationYAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.y,e._accelerationZAvailable=l.motion.data.acceleration&&l.motion.data.acceleration.z,e._accelerationIncludingGravityXAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.x,e._accelerationIncludingGravityYAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.y,e._accelerationIncludingGravityZAvailable=l.motion.data.accelerationIncludingGravity&&l.motion.data.accelerationIncludingGravity.z,e._rotationRateAlphaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.alpha,e._rotationRateBetaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.beta,e._rotationRateGammaAvailable=l.motion.data.rotationRate&&l.motion.data.rotationRate.gamma,b(e)}).catch(function(){e.stop(),d("DeviceMotion is not supported")})});return b},t.Quaternion=function(a,c,d,e){var f;this.set=function(a,b,c,d){this.x=a||0,this.y=b||0,this.z=c||0,this.w=d||1},this.copy=function(a){this.x=a.x,this.y=a.y,this.z=a.z,this.w=a.w},this.setFromEuler=function(){var a,b,c,d,e,f,g,h,i,k,l,m;return function(n){return n=n||{},c=(n.alpha||0)*j,a=(n.beta||0)*j,b=(n.gamma||0)*j,f=c/2,d=a/2,e=b/2,g=Math.cos(d),h=Math.cos(e),i=Math.cos(f),k=Math.sin(d),l=Math.sin(e),m=Math.sin(f),this.set(k*h*i-g*l*m,g*l*i+k*h*m,g*h*m+k*l*i,g*h*i-k*l*m),this.normalize(),this}}(),this.setFromRotationMatrix=function(){var a;return function(c){return a=c.elements,this.set(.5*Math.sqrt(1+a[0]-a[4]-a[8])*b(a[7]-a[5]),.5*Math.sqrt(1-a[0]+a[4]-a[8])*b(a[2]-a[6]),.5*Math.sqrt(1-a[0]-a[4]+a[8])*b(a[3]-a[1]),.5*Math.sqrt(1+a[0]+a[4]+a[8])),this}}(),this.multiply=function(a){return f=t.Quaternion.prototype.multiplyQuaternions(this,a),this.copy(f),this},this.rotateX=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[1,0,0],a),this.copy(f),this},this.rotateY=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[0,1,0],a),this.copy(f),this},this.rotateZ=function(a){return f=t.Quaternion.prototype.rotateByAxisAngle(this,[0,0,1],a),this.copy(f),this},this.normalize=function(){return t.Quaternion.prototype.normalize(this)},this.set(a,c,d,e)},t.Quaternion.prototype={constructor:t.Quaternion,multiplyQuaternions:function(){var a=new t.Quaternion;return function(b,c){var d=b.x,e=b.y,f=b.z,g=b.w,h=c.x,i=c.y,j=c.z,k=c.w;return a.set(d*k+g*h+e*j-f*i,e*k+g*i+f*h-d*j,f*k+g*j+d*i-e*h,g*k-d*h-e*i-f*j),a}}(),normalize:function(a){var b=Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z+a.w*a.w);return 0===b?(a.x=0,a.y=0,a.z=0,a.w=1):(b=1/b,a.x*=b,a.y*=b,a.z*=b,a.w*=b),a},rotateByAxisAngle:function(){var a,b,c=new t.Quaternion,d=new t.Quaternion;return function(e,f,g){return a=(g||0)/2,b=Math.sin(a),d.set((f[0]||0)*b,(f[1]||0)*b,(f[2]||0)*b,Math.cos(a)),c=t.Quaternion.prototype.multiplyQuaternions(e,d),t.Quaternion.prototype.normalize(c)}}()},t.RotationMatrix=function(a,b,c,d,e,f,g,h,i){var k;this.elements=new Float32Array(9),this.identity=function(){return this.set(1,0,0,0,1,0,0,0,1),this},this.set=function(a,b,c,d,e,f,g,h,i){this.elements[0]=a||1,this.elements[1]=b||0,this.elements[2]=c||0,this.elements[3]=d||0,this.elements[4]=e||1,this.elements[5]=f||0,this.elements[6]=g||0,this.elements[7]=h||0,this.elements[8]=i||1},this.copy=function(a){this.elements[0]=a.elements[0],this.elements[1]=a.elements[1],this.elements[2]=a.elements[2],this.elements[3]=a.elements[3],this.elements[4]=a.elements[4],this.elements[5]=a.elements[5],this.elements[6]=a.elements[6],this.elements[7]=a.elements[7],this.elements[8]=a.elements[8]},this.setFromEuler=function(){var a,b,c,d,e,f,g,h,i;return function(k){return k=k||{},c=(k.alpha||0)*j,a=(k.beta||0)*j,b=(k.gamma||0)*j,d=Math.cos(a),e=Math.cos(b),f=Math.cos(c),g=Math.sin(a),h=Math.sin(b),i=Math.sin(c),this.set(f*e-i*g*h,-d*i,e*i*g+f*h,e*i+f*g*h,f*d,i*h-f*e*g,-d*h,g,d*e),this.normalize(),this}}(),this.setFromQuaternion=function(){var a,b,c,d;return function(e){return a=e.w*e.w,b=e.x*e.x,c=e.y*e.y,d=e.z*e.z,this.set(a+b-c-d,2*(e.x*e.y-e.w*e.z),2*(e.x*e.z+e.w*e.y),2*(e.x*e.y+e.w*e.z),a-b+c-d,2*(e.y*e.z-e.w*e.x),2*(e.x*e.z-e.w*e.y),2*(e.y*e.z+e.w*e.x),a-b-c+d),this}}(),this.multiply=function(a){return k=t.RotationMatrix.prototype.multiplyMatrices(this,a),this.copy(k),this},this.rotateX=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[1,0,0],a),this.copy(k),this},this.rotateY=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[0,1,0],a),this.copy(k),this},this.rotateZ=function(a){return k=t.RotationMatrix.prototype.rotateByAxisAngle(this,[0,0,1],a),this.copy(k),this},this.normalize=function(){return t.RotationMatrix.prototype.normalize(this)},this.set(a,b,c,d,e,f,g,h,i)},t.RotationMatrix.prototype={constructor:t.RotationMatrix,multiplyMatrices:function(){var a,b,c=new t.RotationMatrix;return function(d,e){return a=d.elements,b=e.elements,c.set(a[0]*b[0]+a[1]*b[3]+a[2]*b[6],a[0]*b[1]+a[1]*b[4]+a[2]*b[7],a[0]*b[2]+a[1]*b[5]+a[2]*b[8],a[3]*b[0]+a[4]*b[3]+a[5]*b[6],a[3]*b[1]+a[4]*b[4]+a[5]*b[7],a[3]*b[2]+a[4]*b[5]+a[5]*b[8],a[6]*b[0]+a[7]*b[3]+a[8]*b[6],a[6]*b[1]+a[7]*b[4]+a[8]*b[7],a[6]*b[2]+a[7]*b[5]+a[8]*b[8]),c}}(),normalize:function(a){var b=a.elements,c=b[0]*b[4]*b[8]-b[0]*b[5]*b[7]-b[1]*b[3]*b[8]+b[1]*b[5]*b[6]+b[2]*b[3]*b[7]-b[2]*b[4]*b[6];return b[0]/=c,b[1]/=c,b[2]/=c,b[3]/=c,b[4]/=c,b[5]/=c,b[6]/=c,b[7]/=c,b[8]/=c,a.elements=b,a},rotateByAxisAngle:function(){var a,b,c=new t.RotationMatrix,d=new t.RotationMatrix,e=!1;return function(f,g,h){return d.identity(),e=!1,a=Math.sin(h),b=Math.cos(h),1===g[0]&&0===g[1]&&0===g[2]?(e=!0,d.elements[4]=b,d.elements[5]=-a,d.elements[7]=a,d.elements[8]=b):1===g[1]&&0===g[0]&&0===g[2]?(e=!0,d.elements[0]=b,d.elements[2]=a,d.elements[6]=-a,d.elements[8]=b):1===g[2]&&0===g[0]&&0===g[1]&&(e=!0,d.elements[0]=b,d.elements[1]=-a,d.elements[3]=a,d.elements[4]=b),e?(c=t.RotationMatrix.prototype.multiplyMatrices(f,d),c=t.RotationMatrix.prototype.normalize(c)):c=f,c}}()},t.Euler=function(a,b,c){this.set=function(a,b,c){this.alpha=a||0,this.beta=b||0,this.gamma=c||0},this.copy=function(a){this.alpha=a.alpha,this.beta=a.beta,this.gamma=a.gamma},this.setFromRotationMatrix=function(){var a,b,c,d;return function(e){a=e.elements,a[8]>0?(b=Math.atan2(-a[1],a[4]),c=Math.asin(a[7]),d=Math.atan2(-a[6],a[8])):a[8]<0?(b=Math.atan2(a[1],-a[4]),c=-Math.asin(a[7]),c+=c>=0?-g:g,d=Math.atan2(a[6],-a[8])):a[6]>0?(b=Math.atan2(-a[1],a[4]),c=Math.asin(a[7]),d=-h):a[6]<0?(b=Math.atan2(a[1],-a[4]),c=-Math.asin(a[7]),c+=c>=0?-g:g,d=-h):(b=Math.atan2(a[3],a[0]),c=a[7]>0?h:-h,d=0),0>b&&(b+=i),b*=k,c*=k,d*=k,this.set(b,c,d)}}(),this.setFromQuaternion=function(){var a,b,c;return function(d){var e=d.w*d.w,f=d.x*d.x,j=d.y*d.y,l=d.z*d.z,m=e+f+j+l,n=d.w*d.x+d.y*d.z,o=1e-6;if(n>(.5-o)*m)a=2*Math.atan2(d.y,d.w),b=h,c=0;else if((-.5+o)*m>n)a=-2*Math.atan2(d.y,d.w),b=-h,c=0;else{var p=e-f+j-l,q=2*(d.w*d.z-d.x*d.y),r=e-f-j+l,s=2*(d.w*d.y-d.x*d.z);r>0?(a=Math.atan2(q,p),b=Math.asin(2*n/m),c=Math.atan2(s,r)):(a=Math.atan2(-q,-p),b=-Math.asin(2*n/m),b+=0>b?g:-g,c=Math.atan2(-s,-r))}0>a&&(a+=i),a*=k,b*=k,c*=k,this.set(a,b,c)}}(),this.rotateX=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[1,0,0],a),this},this.rotateY=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[0,1,0],a),this},this.rotateZ=function(a){return t.Euler.prototype.rotateByAxisAngle(this,[0,0,1],a),this},this.set(a,b,c)},t.Euler.prototype={constructor:t.Euler,rotateByAxisAngle:function(){var a=new t.RotationMatrix;return function(b,c,d){return a.setFromEuler(b),a=t.RotationMatrix.prototype.rotateByAxisAngle(a,c,d),b.setFromRotationMatrix(a),b}}()},t.DeviceOrientation=function(b){this.options=b||{};var c=0,d=200,e=0,f=10;if(this.alphaOffsetScreen=0,this.alphaOffsetDevice=void 0,"game"===this.options.type){var g=function(b){return null!==b.alpha&&(this.alphaOffsetDevice=new t.Euler(b.alpha,0,0),this.alphaOffsetDevice.rotateZ(-o),++e>=f)?void a.removeEventListener("deviceorientation",g,!1):void(++c>=d&&a.removeEventListener("deviceorientation",g,!1))}.bind(this);a.addEventListener("deviceorientation",g,!1)}else if("world"===this.options.type){var h=function(b){return b.absolute!==!0&&void 0!==b.webkitCompassAccuracy&&null!==b.webkitCompassAccuracy&&+b.webkitCompassAccuracy>=0&&+b.webkitCompassAccuracy<50&&(this.alphaOffsetDevice=new t.Euler(b.webkitCompassHeading,0,0),this.alphaOffsetDevice.rotateZ(o),this.alphaOffsetScreen=o,++e>=f)?void a.removeEventListener("deviceorientation",h,!1):void(++c>=d&&a.removeEventListener("deviceorientation",h,!1))}.bind(this);a.addEventListener("deviceorientation",h,!1)}},t.DeviceOrientation.prototype={constructor:t.DeviceOrientation,start:function(b){b&&"[object Function]"==Object.prototype.toString.call(b)&&l.orientation.callbacks.push(b),m||(n?a.screen.orientation.addEventListener("change",d,!1):a.addEventListener("orientationchange",d,!1)),l.orientation.active||(a.addEventListener("deviceorientation",e,!1),l.orientation.active=!0)},stop:function(){l.orientation.active&&(a.removeEventListener("deviceorientation",e,!1),l.orientation.active=!1)},listen:function(a){this.start(a)},getFixedFrameQuaternion:function(){var a=new t.Euler,b=new t.RotationMatrix,c=new t.Quaternion;return function(){var d=l.orientation.data||{alpha:0,beta:0,gamma:0},e=d.alpha;return this.alphaOffsetDevice&&(b.setFromEuler(this.alphaOffsetDevice),b.rotateZ(-this.alphaOffsetScreen),a.setFromRotationMatrix(b),a.alpha<0&&(a.alpha+=360),e-=a.alpha),a.set(e,d.beta,d.gamma),c.setFromEuler(a),c}}(),getScreenAdjustedQuaternion:function(){var a;return function(){return a=this.getFixedFrameQuaternion(),a.rotateZ(-o),a}}(),getFixedFrameMatrix:function(){var a=new t.Euler,b=new t.RotationMatrix;return function(){var c=l.orientation.data||{alpha:0,beta:0,gamma:0},d=c.alpha;return this.alphaOffsetDevice&&(b.setFromEuler(this.alphaOffsetDevice),b.rotateZ(-this.alphaOffsetScreen),a.setFromRotationMatrix(b),a.alpha<0&&(a.alpha+=360),d-=a.alpha),a.set(d,c.beta,c.gamma),b.setFromEuler(a),b}}(),getScreenAdjustedMatrix:function(){var a;return function(){return a=this.getFixedFrameMatrix(),a.rotateZ(-o),a}}(),getFixedFrameEuler:function(){var a,b=new t.Euler;return function(){return a=this.getFixedFrameMatrix(),b.setFromRotationMatrix(a),b}}(),getScreenAdjustedEuler:function(){var a,b=new t.Euler;return function(){return a=this.getScreenAdjustedMatrix(),b.setFromRotationMatrix(a),b}}(),isAbsolute:function(){return l.orientation.data&&l.orientation.data.absolute===!0?!0:!1},getLastRawEventData:function(){return l.orientation.data||{}},_alphaAvailable:!1,_betaAvailable:!1,_gammaAvailable:!1,isAvailable:function(a){switch(a){case this.ALPHA:return this._alphaAvailable;case this.BETA:return this._betaAvailable;case this.GAMMA:return this._gammaAvailable}},ALPHA:"alpha",BETA:"beta",GAMMA:"gamma"},t.DeviceMotion=function(a){this.options=a||{}},t.DeviceMotion.prototype={constructor:t.DeviceMotion,start:function(b){b&&"[object Function]"==Object.prototype.toString.call(b)&&l.motion.callbacks.push(b),m||(n?a.screen.orientation.addEventListener("change",d,!1):a.addEventListener("orientationchange",d,!1)),l.motion.active||(a.addEventListener("devicemotion",f,!1),l.motion.active=!0)},stop:function(){l.motion.active&&(a.removeEventListener("devicemotion",f,!1),l.motion.active=!1)},listen:function(a){this.start(a)},getScreenAdjustedAcceleration:function(){var a=l.motion.data&&l.motion.data.acceleration?l.motion.data.acceleration:{x:0,y:0,z:0},b={};switch(o){case p:b.x=-a.y,b.y=a.x;break;case q:b.x=-a.x,b.y=-a.y;break;case r:case s:b.x=a.y,b.y=-a.x;break;default:b.x=a.x,b.y=a.y}return b.z=a.z,b},getScreenAdjustedAccelerationIncludingGravity:function(){var a=l.motion.data&&l.motion.data.accelerationIncludingGravity?l.motion.data.accelerationIncludingGravity:{x:0,y:0,z:0},b={};switch(o){case p:b.x=-a.y,b.y=a.x;break;case q:b.x=-a.x,b.y=-a.y;break;case r:case s:b.x=a.y,b.y=-a.x;break;default:b.x=a.x,b.y=a.y}return b.z=a.z,b},getScreenAdjustedRotationRate:function(){var a=l.motion.data&&l.motion.data.rotationRate?l.motion.data.rotationRate:{alpha:0,beta:0,gamma:0},b={};switch(o){case p:b.beta=-a.gamma,b.gamma=a.beta;break;case q:b.beta=-a.beta,b.gamma=-a.gamma;break;case r:case s:b.beta=a.gamma,b.gamma=-a.beta;break;default:b.beta=a.beta,b.gamma=a.gamma}return b.alpha=a.alpha,b},getLastRawEventData:function(){return l.motion.data||{}},_accelerationXAvailable:!1,_accelerationYAvailable:!1,_accelerationZAvailable:!1,_accelerationIncludingGravityXAvailable:!1,_accelerationIncludingGravityYAvailable:!1,_accelerationIncludingGravityZAvailable:!1,_rotationRateAlphaAvailable:!1,_rotationRateBetaAvailable:!1,_rotationRateGammaAvailable:!1,isAvailable:function(a){switch(a){case this.ACCELERATION_X:return this._accelerationXAvailable;case this.ACCELERATION_Y:return this._accelerationYAvailable;case this.ACCELERATION_Z:return this._accelerationZAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_X:return this._accelerationIncludingGravityXAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_Y:return this._accelerationIncludingGravityYAvailable;case this.ACCELERATION_INCLUDING_GRAVITY_Z:return this._accelerationIncludingGravityZAvailable;case this.ROTATION_RATE_ALPHA:return this._rotationRateAlphaAvailable;case this.ROTATION_RATE_BETA:return this._rotationRateBetaAvailable;case this.ROTATION_RATE_GAMMA:return this._rotationRateGammaAvailable}},ACCELERATION_X:"accelerationX",ACCELERATION_Y:"accelerationY",ACCELERATION_Z:"accelerationZ",ACCELERATION_INCLUDING_GRAVITY_X:"accelerationIncludingGravityX",ACCELERATION_INCLUDING_GRAVITY_Y:"accelerationIncludingGravityY",ACCELERATION_INCLUDING_GRAVITY_Z:"accelerationIncludingGravityZ",ROTATION_RATE_ALPHA:"rotationRateAlpha",ROTATION_RATE_BETA:"rotationRateBeta",ROTATION_RATE_GAMMA:"rotationRateGamma"},a.FULLTILT=t}}(window);
#map{
width: 200px; height: 200px; position: absolute; right: 20px; bottom: 20px;
}
#street-view{
width: 100%; height: 100%; position: absolute; top: 0; left: 0;
}
#debug{
position: absolute; top: 0; left: 0; padding: 10px; border: 1px solid white; color: white;
}
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
<div id="street-view"></div>
<div id="map"></div>
<div id="debug">
alpha: <span id="alpha"></span> |
beta: <span id="beta"></span> |
gamma: <span id="gamma"></span>
</div>

Related

konva.js transformer crop box behavior problem

Is there any way to do the same with the cropbox action in the link below?
I am using the latest version of konva, but it seems that the link does not work after version 4.
https://codepen.io/kade87/pen/vYavQMp?editors=1011
How can I use it to work like a link in the latest version?
We must solve this problem. Or is there a javascript crop library that supports both pc and mobile? If so, please recommend. However, most of the libraries didn't work the way we wanted.
<script src="https://unpkg.com/konva#^4/konva.min.js"></script>
<div id="container"></div>
<script>
// noprotect
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight
});
const layer = new Konva.Layer();
stage.add(layer);
Konva.Image.fromURL('https://i.imgur.com/ktWThtZ.png', img => {
img.setAttrs({
width: stage.width(),
height: stage.height(),
opacity: 0.5
});
layer.add(img)
})
Konva.Image.fromURL('https://i.imgur.com/ktWThtZ.png', img => {
var group = new Konva.Group({
clipFunc: (ctx) => {
ctx.save();
ctx.translate(fakeShape.x(), fakeShape.y())
ctx.rotate(Konva.getAngle(fakeShape.rotation()))
ctx.rect(0, 0, fakeShape.width() * fakeShape.scaleX(), fakeShape.height() * fakeShape.scaleY());
ctx.restore()
}
})
layer.add(group);
img.setAttrs({
width: stage.width(),
height: stage.height(),
});
group.add(img);
var fakeShape = new Konva.Rect({
width: 100,
height: 100,
x: 100,
y: 100,
fill: 'rgba(0,0,0,0)',
draggable: true
})
layer.add(fakeShape);
var tr = new Konva.Transformer({
enabledAnchors: ['top-left', 'bottom-right'],
rotateEnabled: false,
keepRatio: false,
flipEnabled: true,
node: fakeShape,
boundBoxFunc: (oldBox, newBox) => {
if(newBox.width < 50) {
newBox.width = 50
// newBox.x = oldBox.x
}
if(newBox.height < 50) {
newBox.height = 50
// newBox.y = oldBox.y
}
return newBox
}
});
layer.add(tr);
layer.draw();
});
</script>

How to make progress bar match curve of parent

I have a toast notification with a progress bar like the following image and I like the rounded corners but I can't figure out how to hide the portion of the loading bar that goes outside the rounded corners. How would I do that given the setup in this example. I would also like to know how I could reverse the direction of the indicator so it starts full and goes toward empty then the notification disappears. Lobibox doesn't appear to have either of these options out of the box but I would really like to add them. Thanks for the help!
Here is a sample of a lobibox notification:
Lobibox.notify('success', {
size: 'mini',
rounded: true,
delayIndicator: true,
msg: 'Project Saved Successfully!',
iconSource: 'fontAwesome',
position: 'top right',
delay: 50000,
});
you can override the css
Lobibox.notify('success', {
size: 'mini',
rounded: true,
delayIndicator: false,
msg: 'Project Saved Successfully!',
iconSource: 'fontAwesome',
position: 'top right',
delay: 20000,
delayIndicator: true
});
body {
background-color: black;
}
.lobibox-notify .lobibox-delay-indicator {
left: 22px !important;
width: 360px;
}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lobibox#1.2.7/dist/css/lobibox.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lobibox#1.2.7/dist/js/lobibox.min.js"></script>
Figured out how to reverse the indicator direction. Find the _addDelay function in the source and overwrite it with my updated version below. This adds the ability to set options.reverseDelayIndicator = true to reverse the direction of the indicator. It also allows you to have the indicator display properly on rounded and square edge notifications if you include the css snippet below in your solution.
var _addDelay = function ($el) {
if (!me.$options.delay) {
return;
}
if (me.$options.delayIndicator) {
var delay = $('<div class="lobibox-delay-indicator"><div></div></div>');
if (me.$options.rounded) {
delay.addClass("lobibox-delay-rounded");
} else {
delay.removeClass("lobibox-delay-rounded");
}
$el.append(delay);
}
var time = 0;
var interval = 1000 / 30;
var currentTime = new Date().getTime();
var timer = setInterval(function () {
if (me.$options.continueDelayOnInactiveTab) {
time = new Date().getTime() - currentTime;
} else {
time += interval;
}
if (me.$options.reverseDelayIndicator) {
var width = 100 - (100 * time / me.$options.delay);
if (width <= 0) {
width = 0;
me.remove();
timer = clearInterval(timer);
}
} else {
var width = 100 * time / me.$options.delay;
if (width >= 100) {
width = 0;
me.remove();
timer = clearInterval(timer);
}
}
if (me.$options.delayIndicator) {
delay.find('div').css('width', width + "%");
}
}, interval);
if (me.$options.pauseDelayOnHover) {
$el.on('mouseenter.lobibox', function () {
interval = 0;
}).on('mouseleave.lobibox', function () {
interval = 1000 / 30;
});
}
};
CSS To allow both rounded and square indicators display properly:
.lobibox-notify .lobibox-delay-indicator.lobibox-delay-rounded {
left: 22px;
width: calc(100% - 44px);
}

Save and load the position of dynamically created, draggable elements(jQuery-UI)

I am in the process of developing an offline web application that mimics a magnet board.
I can dynamicly create the draggable Items and drag them around the screen. Now I want to be able to save the positions and content(just one image per element) of my elements into a file and later load it again.
I want it in a file because it needs to be interchangable between different users.
Here is the javascript of how i dynamicly create my elements:
$("#item1").mousedown(function (e){
var newpin = document.createElement("DIV");
var pinimage = document.createElement("IMG");
pinimage.setAttribute("src", "Media/2D_Container_Alfa.jpg");
pinimage.setAttribute("height", "70px");
newpin.setAttribute("position","relative");
newpin.setAttribute("top","20px");
newpin.setAttribute("left","140px");
newpin.setAttribute("display","block");
newpin.setAttribute("class", "draggable ui-draggable ui-draggable-handle");
newpin.appendChild(pinimage);
document.body.appendChild(newpin);});
TLDR.: I want to save the configuration on my magnet board and be able to load previously saved configurations
You may consider some newer code:
function makePin(event, t, l){
var $pin = $("<div>", {
class: "pin draggable ui-draggable ui-draggable-handle"
}).css({
position: "relative",
top: (t !== undefined ? t + "px" : "20px"),
left: (l !== undefined ? l + "px" : "140px"),
disply: "block"
}).appendTo(".pin-area");
var $pinImage = $("<img>", {
src: "Media/2D_Container_Alfa.jpg"
}).css({
height: "70px"
}).appendTo($pin);
return $pin;
}
function getPins(){
var pins = [];
$(".pin-area .pin").each(function(ind, el){
var pos = $(el).offset();
var imgSrc = $("img", el).attr("src");
pins.push({
top: pos.top,
left: pos.left,
src: imgSrc
});
});
return pins;
}
$("#item1").mousedown(function (e){
makePin(e);
});
Since you did not include any HTML in your post, I would consider the following in relationship to the code above:
<body>
<div class="pin-area">
</div>
</body>
You also did not indicate what would initiate the saving of this data once collected or how it would be loaded. one way to do this is to update once any drag action is completed, binding to stop. You also did not mention where you wanted to store this data. The following makes some assumptions that you will be using Draggable and LocalStorage.
$(".pin").on("dragstop", function(e, ui){
localStorage.setItem("pins", JSON.stringify(getPins()));
});
This will update a locally stored variable with JSON text each time a pin is moved.
function loadPins(){
var pins = JSON.parse(localStorage.getItems("pins"));
if(pins !== undefined){
$.each(pins, function(k, v){
makePin(null, v.top, v.left);
});
}
}
This function would then be able to load those pins. You'd want to execute this function just before you close your jQuery block. A small example of it all put together. You will need to do more testing and adjusting to fit your needs.
$(function() {
function makePin(event, t, l) {
var $pin = $("<div>", {
class: "pin draggable ui-draggable ui-draggable-handle"
}).css({
position: "relative",
top: (t !== undefined ? t + "px" : "20px"),
left: (l !== undefined ? l + "px" : "140px"),
disply: "block"
}).appendTo(".pin-area");
var $pinImage = $("<img>", {
src: $(".example-image").attr("src")
}).css({
height: "70px"
}).appendTo($pin);
$pin.draggable();
return $pin;
}
function getPins() {
var pins = [];
$(".pin-area .pin").each(function(ind, el) {
var pos = $(el).offset();
var imgSrc = $("img", el).attr("src");
pins.push({
top: pos.top,
left: pos.left,
src: imgSrc
});
});
return pins;
}
function loadPins() {
var pins = JSON.parse(localStorage.getItems("pins"));
if (pins !== undefined) {
$.each(pins, function(k, v) {
makePin(null, v.top, v.left);
});
}
}
$(".pin").on("dragstop", function(e, ui) {
localStorage.setItem("pins", JSON.stringify(getPins()));
});
$(".pin-area").mousedown(function(e) {
makePin(e);
});
loadPins();
});
.pin-area {
width: 1200px;
height: 630px;
border: 1px solid #222;
background-image: url("https://www2.lbl.gov/assets/img/maps/sitemap.jpg");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="pin-area">
</div>
<p>Example Pin:</p><img class="example-image" style="height: 70px;" src="https://www.clker.com/cliparts/r/R/m/q/E/6/orange-pin-th.png">

Fabric JS object alignment not working as expected

I tried to align an object within a group. When I align the first object to the left, center or right it was successful, then I tried for the second object, the first object moved by itself!
Please see the image below (GIF):
Fabric JS Object Alignment
I used HTML5 Canvas, fabric JS & jQuery for the development.
Here is the fiddle: https://jsfiddle.net/mt0ccqq2/
Here is the Code:
$(document).ready(function() {
var canvas = new fabric.Canvas("canvas", { preserveObjectStacking: true });
// create rectangle
var rect = new fabric.Rect({
left: 50,
top: 50,
width: 300,
height: 100,
fill: '#ff0000'
});
// create circle
var text = new fabric.Text("Align Me next", {
left: 190,
top: 320,
fontSize: 20
});
// create text
var text2 = new fabric.Text("Align me first", {
left: 100,
top: 200,
fontSize: 20
});
canvas.add(rect, text, text2);
canvas.renderAll();
// GROUP ON SELECTION
canvas.on("selection:created", function(e) {
var activeObj = canvas.getActiveGroup();
if(activeObj.type === "group") {
console.log("Group created");
var groupWidth = e.target.getWidth();
var groupHeight = e.target.getHeight();
e.target.forEachObject(function(obj) {
var itemWidth = obj.getBoundingRect().width;
var itemHeight = obj.getBoundingRect().height;
// ================================
// OBJECT ALIGNMENT: " H-LEFT "
// ================================
$('#objAlignLeft').click(function() {
obj.set({
left: -(groupWidth / 2),
originX: 'left'
});
obj.setCoords();
canvas.renderAll();
});
// ================================
// OBJECT ALIGNMENT: " H-CENTER "
// ================================
$('#objAlignCenter').click(function() {
obj.set({
left: (0 - itemWidth/2),
originX: 'left'
});
obj.setCoords();
canvas.renderAll();
});
// ================================
// OBJECT ALIGNMENT: " H-RIGHT "
// ================================
$('#objAlignRight').click(function() {
obj.set({
left: (groupWidth/2 - itemWidth/2),
originX: 'center'
});
obj.setCoords();
canvas.renderAll();
});
});
}
}); // END OF " SELECTION:CREATED "
});
Any help would be appreciated. Thanks in advance.
I think this is what you're looking for, right?
Here's the tweak to your code...
canvas.on("selection:cleared", function(e) {
$('#objAlignLeft').off('click');
$('#objAlignCenter').off('click');
$('#objAlignRight').off('click');
});
And here's the all important JSFiddle, https://jsfiddle.net/rekrah/xxrqbhph/.
Let me know if you have any further questions. Happy to help!

Geowebcache + Openlayers3 with XYZ source: error by zooming the map

I'm trying the do the same map as here:
Openlayers3: tile grid incorrect with pixelratio=3 using Geoserver/Geowebcache as backend
but using the TMS protocol instead of WMS.
The map works good but there is a little problem by zooming in, only by changing from zoom level 4 to zoom level 5: the map seems to "jump upwards". The problem occurs with all pixel ratio.
This is my source code. Any help is appreciated:
<!DOCTYPE html>
<html>
<head>
<title>WMS Tiles</title>
<link rel="stylesheet" href="https://openlayers.org/en/v3.19.1/css/ol.css" type="text/css">
<!-- The line below is only needed for old environments like Internet Explorer and Android 4.x -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList,URL"></script>
<script src="https://openlayers.org/en/v3.19.1/build/ol.js"></script>
</head>
<style type="text/css">
body { font-family: sans-serif; font-weight: bold; font-size: .8em; }
body { border: 0px; margin: 0px; padding: 0px; }
</style>
<body>
<div id="map" class="map" style="width: 85%; height: 85%;border: 0px; padding: 0px"> </div>
<script>
console.log("DOMContentLoaded");
try {
var config = {
"bounds": {
"left" : 475279.2689196961,
"bottom" : 5473193.572300382,
"right" : 476655.1750108673,
"top" : 5474594.636365802
}
};
var bounds = [config.bounds.left, config.bounds.bottom, config.bounds.right, config.bounds.top];
var resolutions = [
1.4,
0.7,
0.35,
0.175,
0.0875,
0.04375,
];
var tilePixelRatio = 1;
if (ol.has.DEVICE_PIXEL_RATIO > 2.5) {
tilePixelRatio = 3;
} else if (ol.has.DEVICE_PIXEL_RATIO > 1.5) {
tilePixelRatio = 2;
}
var matrixSet = 'unihd15';
// available gridsets in backend: unihd15, unihd15_512 and unihd15_768
if (tilePixelRatio > 1){
matrixSet = matrixSet + '_' + (256 * tilePixelRatio).toString();
}
var res_length = resolutions.length;
var matrixIds = new Array(res_length );
for (var z = 0; z < res_length ; ++z) {
matrixIds[z] = matrixSet + ':'+ z;
}
console.log('matrixSet is: ' + matrixSet);
console.log(matrixIds);
console.log(matrixIds[0] + ' '+ resolutions[0]);
console.log('Center: ' + ol.extent.getCenter(bounds));
console.log('Pixel ratio: ' + window.devicePixelRatio);
console.log('Bounds: ' + bounds);
console.log('TopLeft: ' + ol.extent.getTopLeft(bounds));
var projection = new ol.proj.Projection({
code: 'EPSG:32632',
units: 'm',
extent: [166021.4431, 0.0000, 833978.5569, 9329005.1825]
});
var tileGrid = new ol.tilegrid.TileGrid({
extent: bounds,
resolutions: resolutions,
origin: ol.extent.getTopLeft(bounds),
tileSize: [256, 256]
});
var view = new ol.View({
extent: bounds,
zoom: 0,
center: ol.extent.getCenter(bounds),
projection: projection,
resolutions: resolutions
});
var layerName = 'unihd15:unihd15_0_basemap';
var tms_source = new ol.source.XYZ({
projection: projection,
tileGrid: tileGrid,
tilePixelRatio: tilePixelRatio,
url: 'http://ssp.deepmap.de/geo/gwc/service/tms/1.0.0/' + layerName + '#' + matrixSet + '#png/{z}/{x}/{-y}.png'
});
var layer = new ol.layer.Tile({
source: tms_source,
extent: bounds
});
var map = new ol.Map({
projection: projection,
controls: ol.control.defaults(
{
rotate: false,
attributionOptions: {
collapsible: false
}
}
),
view: view,
layers: [layer],
target: 'map'
});
console.log("no error");
} catch (error) {
console.log("error");
console.log(error);
}
</script>
</body>
</html>
Usually this kind of problem comes from an incorrect extent. To get the correct extent, go to http://ssp.deepmap.de/geo/gwc/service/tms/1.0.0/unihd15:unihd15_0_basemap#EPSG:32632#png (currently not working on your GeoServer, but that's the URL), and use the <BoundingBox> from there.