How can I make camera to autorotate when i hit any obstacle in threejs. I referred the following link
http://threejs.live/#/webgl_raymarching_reflect
where the rendering restarts when it hits any obstacle. I tried implementing in my project but that doesn't works. How can i implement that in my project?
My map.ts code is
private renderer: THREE.WebGLRenderer;
private camera: THREE.PerspectiveCamera;
public scene: THREE.Scene;
public fieldOfView: number = 10;
public nearClippingPane: number = 1;
public farClippingPane: number = 1000;
public controls: THREE.OrbitControls;
#ViewChild('canvas')
private canvasRef: ElementRef;
constructor(public loadingCtrl: LoadingController) {
this.render = this.render.bind(this);
this.onModelLoadingCompleted = this.onModelLoadingCompleted.bind(this);
}
private get canvas(): HTMLCanvasElement {
return this.canvasRef.nativeElement;
}
private createScene() {
this.scene = new THREE.Scene();
var loader = new THREE.ColladaLoader();
loader.load('assets/Buildings/Block.DAE', this.onModelLoadingCompleted);
}
private onModelLoadingCompleted(collada) {
const loading = this.loadingCtrl.create({
content:'Loading Please Wait...'
});
loading.present();
var modelScene = collada.scene;
modelScene.rotation.x = -0.01 * Math.PI;
// modelScene.rotation.z = 0.03 * Math.PI;
this.scene.add(modelScene);
loading.dismiss();
this.render();
}
private createCamera() {
let aspectRatio = this.getAspectRatio();
this.camera = new THREE.PerspectiveCamera(
this.fieldOfView,
aspectRatio,
this.nearClippingPane,
this.farClippingPane
);
// Set position and look at
this.camera.position.x = 1;
this.camera.position.y = 0.4;
this.camera.position.z = 16;
}
private createLight(){
var ambientLight = new THREE.AmbientLight( 0xcccccc, 0.4 );
this.scene.add( ambientLight );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 1, 1, 0 ).normalize();
this.scene.add( directionalLight );
}
private getAspectRatio(): number {
let height = this.canvas.clientHeight;
if (height === 0) {
return 0;
}
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
return this.canvas.clientWidth / this.canvas.clientHeight;
}
private startRendering() {
this.renderer = new THREE.WebGLRenderer({
canvas: this.canvas,
antialias: true
});
this.renderer.setPixelRatio(devicePixelRatio);
this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setClearColor(0xffffff, 1);
this.renderer.autoClear = true;
let component: MapComponent = this;
(function render() {
requestAnimationFrame(render);
component.render();
}());
}
public render() {
this.renderer.render(this.scene, this.camera);
}
public addControls() {
this.controls = new THREE.OrbitControls(this.camera);
this.controls.rotateSpeed = 1.0;
this.controls.zoomSpeed = 1.2;
this.controls.addEventListener('change', this.render);
}
/* EVENTS */
public onMouseDown(event: MouseEvent) {
console.log("onMouseDown");
event.preventDefault();
// Example of mesh selection/pick:
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
mouse.x = (event.clientX / this.renderer.domElement.clientWidth) * 2 - 1;
mouse.y = - (event.clientY / this.renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, this.camera);
var obj: THREE.Object3D[] = [];
this.findAllObjects(obj, this.scene);
var intersects = raycaster.intersectObjects(obj);
console.log("Scene has " + obj.length + " objects");
console.log(intersects.length + " intersected objects found")
intersects.forEach((i) => {
console.log(i.object); // do what you want to do with object
});
}
private findAllObjects(pred: THREE.Object3D[], parent: THREE.Object3D) {
// NOTE: Better to keep separate array of selected objects
if (parent.children.length > 0) {
parent.children.forEach((i) => {
pred.push(i);
this.findAllObjects(pred, i);
});
}
}
public onMouseUp(event: MouseEvent) {
console.log("onMouseUp");
}
#HostListener('window:resize', ['$event'])
public onResize(event: Event) {
this.canvas.style.width = "100%";
this.canvas.style.height = "100%";
console.log("onResize: " + this.canvas.clientWidth + ", " + this.canvas.clientHeight);
this.camera.aspect = this.getAspectRatio();
this.camera.updateProjectionMatrix();
this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
this.render();
}
#HostListener('document:keypress', ['$event'])
public onKeyPress(event: KeyboardEvent) {
console.log("onKeyPress: " + event.key);
}
/* LIFECYCLE */
ngAfterViewInit() {
this.createScene();
this.createCamera();
this.createLight();
this.startRendering();
this.addControls();
}
And map.html is
<canvas #canvas (mousedown)="onMouseDown($event)" (mouseup)="onMouseUp($event)"></canvas>
Use collision detection from three js utilities
Also use reference from this stack thread
https://stackoverflow.com/a/11480717/16768028
Related
I have create a child class from THREE.PerspectiveCamera and would like to add the ability to make the camera move around on the x,z axes, in the browser I get the position of the the from update_position but the camera does not seem to move
class FPS extends THREE.PerspectiveCamera
{
constructor(params)
{
super(params)
this.keys = {
forward: false,
backward: false,
left: false,
right: false,
}
window.addEventListener('keydown', (event)=>
{
console.info(this.keys);
switch (event.keyCode)
{
case 87: // w
this.keys.forward = true;
break;
case 65: // a
this.keys.left = true;
break;
case 83: // s
this.keys.backward = true;
break;
case 68: // d
this.keys.right = true;
break;
}
this.update_position();
});
window.addEventListener('keyup', (event)=>
{
switch (event.keyCode)
{
case 87: // w
this.keys.forward = false;
break;
case 65: // a
this.keys.left = false;
break;
case 83: // s
this.keys.backward = false;
break;
case 68: // d
this.keys.right = false;
break;
}
});
}
update_position()
{
// x axis moves right or left
if (this.keys.right == true)
{
this.position.x += 1;
}
else if (this.keys.left == true)
{
this.position.x -= 1;
}
// z axis moves forward or backwards
if (this.keys.forward == true)
{
this.position.z += 1;
}
else if (this.keys.backward == true)
{
this.position.z -= 1;
}
console.log(this.position.x);
console.log(this.position.y);
console.log(this.position.z);
}
}
class Game
{
constructor()
{
this.scene = new THREE.Scene();
this.camera = new FPS(100, window.Width/window.Height, 0.1, 1000);
render_scene()
{
requestAnimationFrame(() =>
{
this.renderer.render(this.scene , this.camera);
this.render_scene();
});
}
}
if I replace position with rotation then the camera does rotate but it does not work for the position
Your code seems to work as expected:
let camera, scene, renderer, mesh;
function init() {
camera = new FPS(70, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.z = 10;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshNormalMaterial();
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
//
class FPS extends THREE.PerspectiveCamera {
constructor(fov, aspect, near, far) {
super(fov, aspect, near, far)
this.keys = {
forward: false,
backward: false,
left: false,
right: false,
}
window.addEventListener('keydown', (event) => {
//console.info(this.keys);
switch (event.keyCode) {
case 87: // w
this.keys.forward = true;
break;
case 65: // a
this.keys.left = true;
break;
case 83: // s
this.keys.backward = true;
break;
case 68: // d
this.keys.right = true;
break;
}
this.update_position();
});
window.addEventListener('keyup', (event) => {
switch (event.keyCode) {
case 87: // w
this.keys.forward = false;
break;
case 65: // a
this.keys.left = false;
break;
case 83: // s
this.keys.backward = false;
break;
case 68: // d
this.keys.right = false;
break;
}
});
}
update_position() {
// x axis moves right or left
if (this.keys.right == true) {
this.position.x += 1;
} else if (this.keys.left == true) {
this.position.x -= 1;
}
// z axis moves forward or backwards
if (this.keys.forward == true) {
this.position.z += 1;
} else if (this.keys.backward == true) {
this.position.z -= 1;
}
//console.log(this.position.x);
//console.log(this.position.y);
//console.log(this.position.z);
}
}
init();
animate();
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three#0.145/build/three.min.js"></script>
The only thing I have updated in FPS was the constructor since the arguments were not applied correctly. Better to explicitly list the parameters.
This is the current vertical image slider URL.
current vertical image slider URL.
I want to change the slider direction to horizontal.
These are my codes.
right view.js
import { View } from './view.js'
class SliderAnimator {
// Ramon: Frame count of animation
static MAX_COUNT = 50
constructor(view) {
this.view = view
this.running = false
this.view.plane.children[0].material.uniforms.hidden.value = false
}
update() {
if (this.running) {
this.stepCount++
this.view.divider = this.from + (this.to - this.from) /
SliderAnimator.MAX_COUNT * this.stepCount
// this.view.plane.children[0].material.uniforms.divider.value = this.view.divider
// this.view.plane.children[0].material.uniforms.hidden.value = false
if (this.stepCount == SliderAnimator.MAX_COUNT) {
this.running = false
// this.view.plane.children[0].material.uniforms.hidden.value = true
}
}
}
}
export class TopRightView extends View {
static DIVIDER_THRESHOLD = 30
async init() {
await super.init()
let textureLoader = new THREE.TextureLoader()
let texture0 = textureLoader.setPath('./images/jpeg/').load(param.JPEG_FILES[this.id])
let texture1 = textureLoader.setPath('./images/jpeg/').load(param.JPEG_FILES[4])
this.plane.children[0].material = new THREE.ShaderMaterial({
uniforms: {
divider: { value: 0 },
hidden: {value: true },
zoomFactor: { value: 1.0 },
tex0: { type: "t", value: texture0 },
tex1: { type: "t", value: texture1 }
},
vertexShader: this.vertexShader(),
fragmentShader: this.fragmentShader()
})
// this.divider = this.planeWidth / 2
this.divider = 0
this.plane.children[0].material.uniforms.divider.value = this.divider
// this.plane.children[0].material.uniforms.divider.value = 1.3333
this.dividerMoving = false
this.snappable = false
this.sliderAnimator = new SliderAnimator(this)
}
vertexShader() {
return `
varying vec2 vUv;
varying vec3 vPosition;
void main() {
vUv = uv;
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`
}
fragmentShader() {
return `
varying vec2 vUv;
varying vec3 vPosition;
uniform sampler2D tex0;
uniform sampler2D tex1;
uniform float divider;
uniform float zoomFactor;
uniform bool hidden;
void main() {
float dividerWidth;
if (hidden) {
dividerWidth = 0.0;
} else {
dividerWidth = 0.03 / zoomFactor;
}
if (vPosition.x > divider + dividerWidth) {
gl_FragColor = texture2D(tex1, vUv);
} else if (vPosition.x < divider - dividerWidth) {
gl_FragColor = texture2D(tex0, vUv);
} else {
gl_FragColor = vec4(0.5, 0.5, 1.0, 1.0);
}
}
`
}
}
views.js
import { SVGPlane } from './svg-plane.js';
export class View {
static DRAG_TRESHOLD = 3;
constructor(id, containter, vector, elementControl) {
this.id = id;
this.width = window.innerWidth - 2 * param.cLeft;
this.height = param.cHeight * 2 + param.cTop;
this.container = containter;
this.vector = vector;
this.elementControl = elementControl;
this.mouseDownPosition = new THREE.Vector2();
}
async init() {
this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
this.renderer.setSize(this.width, this.height);
this.container.appendChild(this.renderer.domElement);
// this.renderer.setClearColor(param.BACK_COLORS[this.id], 0);
this.camera = new THREE.OrthographicCamera(
param.orthoWidth / (-2),
param.orthoWidth / 2,
param.orthoHeight / 2,
param.orthoHeight / (-2),
0.05, 1000
);
this.camera.position.fromArray(param.CAMERA_POSITIONS[this.id]);
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color(param.BACK_COLORS[this.id]);
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableRotate = false;
await this._initMeshes();
this._calibrateCamera();
this.animate();
this.container.addEventListener('mousemove', (event) => { this.onMouseMove(event) }, false);
this.container.addEventListener('mousedown', (event) => { this.onMouseDown(event) }, false);
this.container.addEventListener('mouseup', (event) => { this.onMouseUp(event) }, false);
this.container.addEventListener('mouseout', (event) => { this.onMouseUp(event) }, false);
this.renderer.domElement.addEventListener(
'wheel', (event) => { this.onWheel(event) }, false
);
}
_calibrateCamera() {
this.camera.left = -this.planeWidth / 2;
this.camera.right = this.planeWidth / 2;
this.camera.top = this.planeWidth / 2 * this.height / this.width;
this.camera.bottom = -this.camera.top;
this.camera.updateProjectionMatrix();
}
async _initMeshes() {
this.plane = new THREE.Group();
var imageMesh = await this._getImageMesh();
imageMesh.name = "plane";
this.plane.add(imageMesh);
var helper = new THREE.GridHelper(400, 800);
helper.material.opacity = 0.75;
helper.material.transparent = true;
helper.material.depthTest = false;
helper.position.y = 0.1;
this.plane.add(helper);
this.plane.rotation.fromArray(param.PLANE_ROTATIONS[this.id]);
this.scene.add(this.plane);
}
async _getImageMesh() {
if (param.SVG_MODE) {
return await new SVGPlane().createSVGPlane(param.SVG_FILES[this.id]);
} else {
return new Promise((resolve, reject) => {
new THREE.TextureLoader().setPath('./images/jpeg/').load(
param.JPEG_FILES[this.id],
(t) => {
var s = param.JPEG_SCALES[this.id];
this.planeWidth = s * param.pLength;
this.planeHeight = s * param.pLength * t.image.height / t.image.width;
console.log(this.planeWidth, this.planeHeight);
var planeGeo = new THREE.BoxGeometry(
this.planeWidth, 0.01, this.planeHeight
);
var planeMat = new THREE.MeshPhongMaterial({ map: t });
var planeMesh = new THREE.Mesh(planeGeo, planeMat);
planeMesh.position.copy(param.JPEG_OFFSETS[this.id]);
resolve(planeMesh);
});
});
}
}
animate() {
window.requestAnimationFrame(() => { this.animate(); });
this.renderer.clear();
this.renderer.render(this.scene, this.camera);
}
}
parameter.js
class Parameter {
TOP_LEFT = 0;
TOP_RIGHT = 1;
BOTTOM_LEFT = 2;
BOTTOM_RIGHT = 3;
TO_METER = 0.3048;
PLANE_COLOR = 0xaaaaaa;
BALL_COLOR = 0x0000ff;
LINE_COLOR = 0xff0000;
BACK_COLORS = [
0x598d4b,
0x598d4b,
0xc6ced1,
0xc6ced1
];
MAP_SCALE = 100 / 1.5 * 100 / 53; // no meaning anymore
SVG_MODE = false;
JPEG_FILES = [
'1-Top-Left-Quadrant.jpg',
'2-Top-Right-Quadrant.jpg',
'3-Bottom-Left-Quadrant.jpg',
'4-Bottom-Right-Quadrant.jpg',
'2-Top-Right-Quadrant-2.jpg'
];
SVG_FILES = [
'1-Top-Left-Quadrant.svg',
'2-Top-Right-Quadrant.svg',
'3-Bottom-Left-Quadrant.svg',
'4-Bottom-Right-Quadrant.svg'
];
CAMERA_POSITIONS = [
[ -5, 5, 5],
[ 0, 10, 0],
[-10, 0, 0],
[ 0, 0, 10]
];
PLANE_ROTATIONS = [
[0, 0, 0],
[0, 0, 0],
[Math.PI / 2, 0, Math.PI / 2],
[Math.PI / 2, 0, 0]
];
// PLANE_AXES = ['', 'xz', 'yz', 'xy'];
JPEG_SCALES = [0.8267, 1, 0.95, 0.5688];
JPEG_OFFSETS = [
new THREE.Vector3(0.4, 0, -0.3),
new THREE.Vector3(),
new THREE.Vector3(0, 0, -0.73),
new THREE.Vector3(0.9, 0, -0.98),
];
CAMERA_OFFSETS = [
this.JPEG_OFFSETS[0],
this.JPEG_OFFSETS[1],
new THREE.Vector3(-this.JPEG_OFFSETS[2].z, 0, this.JPEG_OFFSETS[2].x),
new THREE.Vector3(this.JPEG_OFFSETS[3].x, 0, -this.JPEG_OFFSETS[3].z)
]
BALL_SIZE = 0.05;
constructor() {
if (!!Parameter.instance) {
return Parameter.instance;
}
Parameter.instance = this;
this._init();
return this;
}
_init() {
var controlBarHeight = 0;
var gap = 2;
this.cWidth = (window.innerWidth - gap * 3) / 2;
this.cHeight = (window.innerHeight - controlBarHeight - gap * 3) / 2;
this.cTop = gap;
this.cLeft = gap;
this.cRate = this.cWidth / this.cHeight;
this.orthoHeight = 8;
this.orthoWidth = this.orthoHeight * this.cRate;
this.pLength = 8;
this.hLength = 5;
}
}
var param = new Parameter();
Please help me.
I have been trying to figure out what I am doing wrong when calculation the position and size of the mask rectangle once the target image has been resized.
Below is the documentation from fabric JS:
clipPath :fabric.Object
a fabricObject that, without stroke define a clipping area with their shape. filled in black the clipPath object gets used when the object has rendered, and the context is placed in the center of the object cacheCanvas. If you want 0,0 of a clipPath to align with an object center, use clipPath.originX/Y to 'center'
Type:
fabric.Object
Source:
fabric.js, line 12991
The code I have works perfectly when the image is not resized (scale 1:1 X & Y). In the code's function named rescaleMask I attempt to position the mask to a zero center X & Y and when I run my math manually on a piece of graph paper it appears the math is correct. Obviously there is a piece that I am unaware of that is causing the positioning to be off in different ways depending on the quadrant in which the crop is being performed. There is quite a bit of code here but it is important that the mask is created dynamically and not hard coded. The problem must be in the rescaleMask function so hopefully the rest of the code can be ignored.
I created a test image graph with numbered squares which I will crop by clicking the mask button, drawing a rectangle around one of the boxes with the mouse left button and then clicking the crop button. The problem occurs when you resize the image before creating the mask and cropping.
Here is the test image:
Here is a jsfiddle fabric Creating rect with a mouse dynamic js 2.4.1 sent as fix #4
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<button id="mask">Mask</button>
<button id="crop">Crop</button>
JS
var lastSelectedPicture = null;
var isInsertingCropRectangle = false;
var canvas = new fabric.Canvas('c', {
selection: true,
preserveObjectStacking: true,
height: 700,
width: 800
});
var crop_rect, isDown, origX, origY, mask, target;
var done = false;
var src = "https://stealth-apsvaw.streamhoster.com/fabric_js_2_4_1_crop_test/graph_paper_540.png";
fabric.Image.fromURL(src, function(img) {
img.selectable = true;
img.id = 'target';
img.top = 30;
img.left = 30;
canvas.add(img);
});
canvas.on('object:added', function(e) {
target = null;
mask = null;
canvas.forEachObject(function(obj) {
//alert(obj.get('id'));
var id = obj.get('id');
if (id === 'target') {
target = obj;
canvas.setActiveObject(obj);
}
if (id === 'mask') {
//alert(done);
//alert('mask');
mask = obj;
}
});
});
canvas.on('object:modified', function(e) {
e.target.setCoords();
canvas.renderAll();
});
//////////////////////////////////////////////////////////
// MASK
//////////////////////////////////////////////////////////
document.getElementById("mask").addEventListener("click", function() {
isInsertingCropRectangle = true;
canvas.discardActiveObject();
lastSelectedPicture.selectable = false;
lastSelectedPicture.setCoords();
lastSelectedPicture.dirty = true;
canvas.renderAll();
canvas.discardActiveObject();
isInsertingCropRectangle = true;
});
//////////////////////////////////////////////////////////
// CROP
//////////////////////////////////////////////////////////
document.getElementById("crop").addEventListener("click", function() {
if (target !== null && mask !== null) {
target.setCoords();
// Re-scale mask
mask = rescaleMask(target, mask);
mask.setCoords();
// Do the crop
target.clipPath = mask;
target.dirty=true;
canvas.setActiveObject(target);
canvas.bringToFront(target);
target.selectable = true;
canvas.remove(mask);
canvas.renderAll();
console.log(target);
}
});
//////////////////////////////////////////////////////////
// RE-SCALE MASK FOR CROPPING
// P R O B L E M I N T H I S F U N C T I O N
//////////////////////////////////////////////////////////
function rescaleMask(target, mask){
mask.scaleX = 1;
mask.scaleY = 1;
var targetCenterX = target.width * target.scaleX / 2;
var targetCenterY = target.height * target.scaleY / 2;
var maskOverlapX = mask.left - target.left;
var maskOverlapY = mask.top - target.top;
var centerBasedX = maskOverlapX - targetCenterX;
var centerBasedY = maskOverlapY - targetCenterY;
if( maskOverlapX >= targetCenterX){
centerBasedX = maskOverlapX - targetCenterX;
}
else{
centerBasedX = -(targetCenterX) + maskOverlapX;
}
if( maskOverlapY >= targetCenterY){
centerBasedY = maskOverlapY - targetCenterY;
}
else{
centerBasedY = -(targetCenterY) + maskOverlapY;
}
console.log('targetleft = '+target.left);
console.log('targettop = '+target.top);
console.log('targetCenterX = '+targetCenterX);
console.log('targetCenterY = '+targetCenterY);
console.log('maskleft = '+mask.left);
console.log('masktop = '+mask.top);
console.log('maskOverlapX = '+maskOverlapX);
console.log('maskOverlapY = '+maskOverlapY);
console.log('centerBasedX = '+centerBasedX);
console.log('centerBasedY = '+centerBasedY);
mask.left = centerBasedX;
mask.top = centerBasedY;
mask.originX = 'left';
mask.originY = 'top';
mask.setCoords();
mask.dirty=true;
canvas.renderAll();
//var newMask = mask;
return(mask);
}
canvas.on('mouse:down', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse down done = '+done);
if (done) {
canvas.renderAll();
return;
}
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
crop_rect = new fabric.Rect({
left: origX,
top: origY,
width: pointer.x - origX,
height: pointer.y - origY,
opacity: .3,
transparentCorners: false,
selectable: true,
id: 'mask'
});
canvas.add(crop_rect);
canvas.renderAll();
}
else{
}
});
canvas.on('mouse:move', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse move done = '+done);
if (done) {
canvas.renderAll();
return;
}
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
if (origX > pointer.x) {
crop_rect.set({
left: Math.abs(pointer.x)
});
}
if (origY > pointer.y) {
crop_rect.set({
top: Math.abs(pointer.y)
});
}
crop_rect.set({
width: Math.abs(origX - pointer.x)
});
crop_rect.set({
height: Math.abs(origY - pointer.y)
});
crop_rect.setCoords();
canvas.renderAll();
}
else{
}
});
canvas.on('mouse:up', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse up done = '+done);
if (done) {
canvas.renderAll();
return;
}
isDown = false;
crop_rect.set({
selectable: true
});
done = true;
}
else{
}
});
canvas.on('selection:created', function(event) {
console.log("canvas.on('selection:created'");
selectionChanged(event);
});
canvas.on('selection:updated', function(event) {
console.log("canvas.on('selection:updated'");
selectionChanged(event);
});
function selectionChanged(event){
console.log("selectionChanged");
console.log("selectionChanged type = "+event.target.type);
switch(event.target.type) {
case 'textbox':
break;
case 'image':
lastSelectedPicture = event.target;
break;
case 'rect':
break;
case 'group':
break;
default:
break;
}
}
You need to take in consideration the target.scaleX and target.scaleY for mask.
var lastSelectedPicture = null;
var isInsertingCropRectangle = false;
canvas = new fabric.Canvas('c', {
selection: true,
preserveObjectStacking: true,
height: 700,
width: 800
});
var crop_rect, isDown, origX, origY, mask, target;
var done = false;
var src = "https://stealth-apsvaw.streamhoster.com/fabric_js_2_4_1_crop_test/graph_paper_540.png";
fabric.Image.fromURL(src, function(img) {
img.selectable = true;
img.id = 'target';
img.top = 30;
img.left = 30;
canvas.add(img);
});
canvas.on('object:added', function(e) {
target = null;
mask = null;
canvas.forEachObject(function(obj) {
//alert(obj.get('id'));
var id = obj.get('id');
if (id === 'target') {
target = obj;
canvas.setActiveObject(obj);
}
if (id === 'mask') {
//alert(done);
//alert('mask');
mask = obj;
}
});
});
canvas.on('object:modified', function(e) {
e.target.setCoords();
canvas.renderAll();
});
//////////////////////////////////////////////////////////
// MASK
//////////////////////////////////////////////////////////
document.getElementById("mask").addEventListener("click", function() {
isInsertingCropRectangle = true;
canvas.discardActiveObject();
lastSelectedPicture.selectable = false;
lastSelectedPicture.setCoords();
lastSelectedPicture.dirty = true;
canvas.renderAll();
canvas.discardActiveObject();
isInsertingCropRectangle = true;
});
//////////////////////////////////////////////////////////
// CROP
//////////////////////////////////////////////////////////
document.getElementById("crop").addEventListener("click", function() {
if (target !== null && mask !== null) {
target.setCoords();
// Re-scale mask
mask = rescaleMask(target, mask);
mask.setCoords();
// Do the crop
target.clipPath = mask;
target.dirty=true;
canvas.setActiveObject(target);
canvas.bringToFront(target);
target.selectable = true;
canvas.remove(mask);
canvas.renderAll();
console.log(target);
}
});
//////////////////////////////////////////////////////////
// RE-SCALE MASK FOR CROPPING
// P R O B L E M I N T H I S F U N C T I O N
//////////////////////////////////////////////////////////
function rescaleMask(target, mask){
mask.scaleX = 1;
mask.scaleY = 1;
mask.scaleX/=target.scaleX;
mask.scaleY/=target.scaleY;
var targetCenterX = target.width * target.scaleX / 2;
var targetCenterY = target.height * target.scaleY / 2;
var maskOverlapX = mask.left - target.left;
var maskOverlapY = mask.top - target.top;
var centerBasedX = maskOverlapX - targetCenterX;
var centerBasedY = maskOverlapY - targetCenterY;
if( maskOverlapX >= targetCenterX){
centerBasedX = (maskOverlapX - targetCenterX)/target.scaleX;
}
else{
centerBasedX = (-(targetCenterX) + maskOverlapX)/target.scaleX;
}
if( maskOverlapY >= targetCenterY){
centerBasedY = (maskOverlapY - targetCenterY)/target.scaleY;
}
else{
centerBasedY = (-(targetCenterY) + maskOverlapY)/target.scaleY;
}
console.log('targetleft = '+target.left);
console.log('targettop = '+target.top);
console.log('targetCenterX = '+targetCenterX);
console.log('targetCenterY = '+targetCenterY);
console.log('maskleft = '+mask.left);
console.log('masktop = '+mask.top);
console.log('maskOverlapX = '+maskOverlapX);
console.log('maskOverlapY = '+maskOverlapY);
console.log('centerBasedX = '+centerBasedX);
console.log('centerBasedY = '+centerBasedY);
mask.left = centerBasedX;
mask.top = centerBasedY;
mask.originX = 'left';
mask.originY = 'top';
mask.setCoords();
mask.dirty=true;
canvas.renderAll();
//var newMask = mask;
return(mask);
}
canvas.on('mouse:down', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse down done = '+done);
if (done) {
canvas.renderAll();
return;
}
isDown = true;
var pointer = canvas.getPointer(o.e);
origX = pointer.x;
origY = pointer.y;
crop_rect = new fabric.Rect({
left: origX,
top: origY,
width: pointer.x - origX,
height: pointer.y - origY,
opacity: .3,
transparentCorners: false,
selectable: true,
id: 'mask'
});
canvas.add(crop_rect);
canvas.renderAll();
}
else{
}
});
canvas.on('mouse:move', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse move done = '+done);
if (done) {
canvas.renderAll();
return;
}
if (!isDown) return;
var pointer = canvas.getPointer(o.e);
if (origX > pointer.x) {
crop_rect.set({
left: Math.abs(pointer.x)
});
}
if (origY > pointer.y) {
crop_rect.set({
top: Math.abs(pointer.y)
});
}
crop_rect.set({
width: Math.abs(origX - pointer.x)
});
crop_rect.set({
height: Math.abs(origY - pointer.y)
});
crop_rect.setCoords();
canvas.renderAll();
}
else{
}
});
canvas.on('mouse:up', function(o) {
if( isInsertingCropRectangle == true ){
console.log('mouse up done = '+done);
if (done) {
canvas.renderAll();
return;
}
isDown = false;
crop_rect.set({
selectable: true
});
done = true;
}
else{
}
});
canvas.on('selection:created', function(event) {
console.log("canvas.on('selection:created'");
selectionChanged(event);
});
canvas.on('selection:updated', function(event) {
console.log("canvas.on('selection:updated'");
selectionChanged(event);
});
function selectionChanged(event){
console.log("selectionChanged");
console.log("selectionChanged type = "+event.target.type);
switch(event.target.type) {
case 'textbox':
break;
case 'image':
lastSelectedPicture = event.target;
break;
case 'rect':
break;
case 'group':
break;
default:
break;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.1/fabric.min.js"></script>
<canvas id="c" width="500" height="500" style="border:1px solid #ccc"></canvas>
<button id="mask">Mask</button>
<button id="crop">Crop</button>
I'm rendering a wheel in a WebGL canvas using mrdoob's THREE.js.
I want the wheel to
Spin around it's center
Be draggable by mouse or touch interaction
Slow down by applying fake friction
Snap to the center of a wedge whenever the rotation speed reaches a certain threshold.
You may think of the behaviour of the wheel as that of a lottery wheel.
So far I have achieved points 1-3. This is my code:
'use strict';
var WIDTH = 1080,
HEIGHT = 1080;
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
var camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
var scene = new THREE.Scene();
scene.add(camera);
camera.position.z = 300;
// Create renderer
var container = document.querySelector('#test');
var renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH, HEIGHT);
renderer.setClearColor(0x000000, 0);
container.appendChild(renderer.domElement);
// Create objects
var wheelMaterial = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('wheel.png'),
depthWrite: false,
alphaTest: 0.5
});
wheelMaterial.overdraw = true;
var wheel = new THREE.Mesh(
new THREE.PlaneGeometry(240, 240),
wheelMaterial);
scene.add(wheel);
// Mouse interaction
var isDragging = false;
var lastMouseCoords = null;
var mouseCoords = null;
container.addEventListener('mousedown', onDragStart, false);
container.addEventListener('touchstart', onDragStart, false);
container.addEventListener('mouseup', onDragEnd, false);
container.addEventListener('mouseout', onDragEnd, false);
container.addEventListener('touchend', onDragEnd, false);
container.addEventListener('mousemove', onMouseMove, false);
container.addEventListener('touchmove', onMouseMove, false);
function onDragStart(e) {
isDragging = true;
console.log('Dragging', e);
mouseCoords = pageCoordsToCanvasCoords(e);
rotationHistory = [];
}
function onDragEnd(e) {
isDragging = false;
lastMouseCoords = null;
mouseCoords = null;
console.log('Drag end');
}
function onMouseMove(e) {
e.preventDefault();
mouseCoords = pageCoordsToCanvasCoords(e);
}
// Utility functions
function pageCoordsToCanvasCoords(e) {
var canvasX;
var canvasY;
if ('touches' in e && e.touches.length > 0) {
canvasX = e.touches[0].pageX;
canvasY = e.touches[0].pageY;
} else {
canvasX = e.pageX
canvasY = e.pageY
}
canvasX -= e.target.offsetLeft;
canvasY -= e.target.offsetTop;
console.log(canvasX, canvasY);
return {
x: canvasX,
y: canvasY
};
}
function mouseCoordsToRotation(x, y) {
var origoX = WIDTH / 2.0,
origoY = HEIGHT / 2.0;
x = x - origoX;
y = y - origoY;
var atan = Math.atan2(x, y);
return atan;
}
function getMeanVelocity(history) {
if (history.length <= 1) {
return 0;
}
var movements = [];
var startTime = history[0].time;
var totalTimeElapsed = 0;
// Start from the second item in deltaRadians
for (var i = 1; i < history.length; i++) {
var item = history[i];
var movement = item.deltaRad;
movements.push(item.deltaRad);
var movementTimeDelta = item.time - startTime - totalTimeElapsed;
if (movementTimeDelta < 0) {
console.error('movementTimeDelta for history entry #' +
i + ' has travelled back in time somehow.');
}
totalTimeElapsed += movementTimeDelta;
}
var sum = movements.reduce(function (a, b) {
return a + b;
});
return sum / totalTimeElapsed;
}
function applyFakeFriction(velocity, time) {
/*
var currentRotation = wheel.rotation.z;
var nearestBorder = 0;
var nearestBorderDistance = 100;
for (var i = 0; i < PARTITIONS; i++) {
var partition = PARTITION_ARC * i - PARTITION_ARC * PARTITIONS / 2;
var distance = currentRotation - partition;
if (distance < 0) {
distance /= -1;
}
if (distance < nearestBorderDistance) {
console.log('distance is less than nearestBorderDistance')
nearestBorder = partition;
nearestBorderDistance = distance;
if (nearestBorderDistance < 0) {
nearestBorderDistance /= -1;
}
}
}
console.log('nearestBorderDistance: ', nearestBorderDistance);
*/
for (var i = 0; i < time; i++) {
velocity -= WHEEL_FRICTION; // * (nearestBorderDistance * BORDER_FRICTION);
}
return velocity;
}
var rotation = 1;
function snap() {
isSnapping = true;
/* Disabled, this the issue I'm asking about in the post
var update = function () {
cube.position.rotation = current.rotation;
}
var current = {
rotation: rotation
};
TWEEN.removeAll();
var easing = TWEEN.Easing['Elastic']['EaseInOut'];
var tweenHead = neww TWEEN.Tween(current)
.to({rotation: rotation})
.easing(easing)
.onUpdate(update);
tweenHead.start();
*/
}
var rotationHistory = []
var ROTATION_HISTORY_MAX_LENGTH = 5;
var WHEEL_FRICTION = 0.000001;
var BORDER_FRICTION = 2;
var PARTITIONS = 12;
var PARTITION_ARC = 1 * Math.PI / (PARTITIONS / 2); // The width of each section
var wheelVelocity = 0.1;
var wheelSlowDownVelocity = 0;
var lastWheelRotationTime;
var isSnapping = false;
// Render
function tick() {
// Rotate wheel
var currentTime = (new Date).getTime();
if (lastMouseCoords && isDragging) {
// Reset the velocity for the slowdown
wheelSlowDownVelocity = 0;
// Get the delta rotation since last mouse coordinates
var deltaRadians = mouseCoordsToRotation(mouseCoords.x, mouseCoords.y)
- mouseCoordsToRotation(lastMouseCoords.x, lastMouseCoords.y);
// Set the wheel rotation
wheel.rotation.z += deltaRadians;
// Save the rotation in the history and remove any excessive elements
rotationHistory.push({
time: currentTime,
deltaRad: deltaRadians
});
while (rotationHistory.length > ROTATION_HISTORY_MAX_LENGTH) {
rotationHistory.shift();
}
}
if (isDragging) {
lastMouseCoords = mouseCoords;
}
// Continue rotation of the released wheel
if (!isDragging && !lastMouseCoords && lastWheelRotationTime) {
var delta = currentTime - lastWheelRotationTime;
if (wheelSlowDownVelocity == 0) {
var meanVelocityOverTime = getMeanVelocity(rotationHistory);
wheelSlowDownVelocity = meanVelocityOverTime;
} else {
var currentIsNegative = wheelSlowDownVelocity < 0 ? true : false;
var currentVelocity = wheelSlowDownVelocity;
if (currentIsNegative) {
currentVelocity /= -1;
}
console.log('Current velocity: ', currentVelocity);
console.log('delta: ', delta);
var newVelocity = applyFakeFriction(currentVelocity,
delta);
console.log('New velocity: ', newVelocity);
if (newVelocity < 0) {
wheelSlowDownVelocity = 0;
rotationHistory = [];
} else {
if (currentIsNegative) {
// Revert to old polarity
newVelocity /= -1;
}
wheelSlowDownVelocity = newVelocity;
}
}
wheel.rotation.z += wheelSlowDownVelocity * delta;
}
while (wheel.rotation.z > 2 * Math.PI) {
console.log('Correcting rotation: ', wheel.rotation.z);
wheel.rotation.z -= 2 * Math.PI;
}
while (wheel.rotation.z < - (2 * Math.PI)) {
console.log('Correcting rotation: ', wheel.rotation.z);
wheel.rotation.z += 2 * Math.PI;
}
// Update the history record
lastWheelRotationTime = currentTime;
// Render scene and attach render callback to next animation frame.
renderer.render(scene, camera);
window.requestAnimationFrame(tick);
}
tick();
I have the complete code, minus wheel.png over at https://gist.github.com/joar/5747498.
I have been searching for examples of this behaviour but this far I haven't found any.
Note to editors. Please do not change the tags of this post. tween.js != TweenJS.
I have solved the issue.
'use strict';
function Wheel (element, options) {
var self = this;
// Variable initialization
var WIDTH = options.width;
var HEIGHT = options.height;
if (!options.image) {
throw new Error('Image argument missing');
}
var image = options.image;
var showStats = options.showStats || options.stats;
// Core variables
var stats;
var wheel;
var domElement;
var scene;
var camera;
var renderer;
var rotationHistory;
var input;
var animate;
var run = false;
var ROTATION_HISTORY_MAX_LENGTH = 5;
switch (typeof element) {
case 'string':
domElement = document.querySelector(element);
break;
default:
if ('className' in element) {
domElement = element;
} else {
throw new Error('Invalid element: ', element);
}
}
if (typeof element == 'undefined') {
throw new Error('Invalid element.')
}
/* Initializes the WebGL canvas with the wheel plane */
function setupScene() {
var VIEW_ANGLE = 45,
ASPECT = WIDTH / HEIGHT,
NEAR = 0.1,
FAR = 10000;
camera = new THREE.PerspectiveCamera(
VIEW_ANGLE,
ASPECT,
NEAR,
FAR);
scene = new THREE.Scene();
scene.add(camera);
camera.position.z = 300;
// Create renderer
var container = domElement;
renderer = new THREE.WebGLRenderer();
renderer.setSize(WIDTH * 2, HEIGHT * 2);
renderer.setClearColor(0x000000, 0);
// Create objects
var wheelMaterial = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture(image),
depthWrite: false,
alphaTest: 0.5
});
wheelMaterial.overdraw = true;
wheel = new THREE.Mesh(
new THREE.PlaneGeometry(245, 245),
wheelMaterial);
scene.add(wheel);
container.appendChild(renderer.domElement);
}
function setupStats() {
// Init stats
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
}
function setup() {
setupScene();
if (showStats) {
setupStats();
}
}
setup();
// The tick function
function update() {
animate.update(); // Process interactions
self.renderer.render(self.scene, self.camera);
if (showStats) {
self.stats.update();
}
if (run) {
window.requestAnimationFrame(update);
}
}
animate = new Animate();
// Start and run the wheel every animationframe
function start() {
self.input = input = new Input(); // Start capturing input
run = true;
update();
}
/**
* Animate the wheel
*/
function Animate() {
var self = this;
self.velocity = 0;
var velocityPositive = 0;
self.friction = 0.001;
self.snapThreshold = 0.03;
self.isSnapping = false;
var lastAnimationTime;
self.tween;
var rotationHistory = [];
var PARTITIONS = 12;
var PARTITION_ARC = 1 * Math.PI / (PARTITIONS / 2); // The width of each section
function update() {
var currentTime = (new Date).getTime();
velocityPositive = self.velocity;
if (velocityPositive < 0) {
velocityPositive /= -1;
}
if (!self.isSnapping
&& !input.isDragging
&& velocityPositive < self.snapThreshold
&& velocityPositive > 0
&& lastAnimationTime) {
rotationHistory = [];
snap();
}
if (input.isDragging) {
self.isSnapping = false;
TWEEN.removeAll();
}
if (!self.isSnapping) {
/**
* If the mouse is dragging the wheel
*/
if (input.lastMouseCoords && input.isDragging && !input.isSnapping) {
// Reset the velocity for the slowdown
self.velocity = 0;
// Get the delta rotation since last mouse coordinates
var deltaRadians = input.mouseCoordsToRadian(
input.mouseCoords.x, input.mouseCoords.y)
- input.mouseCoordsToRadian(
input.lastMouseCoords.x,
input.lastMouseCoords.y);
// Set the wheel rotation
wheel.rotation.z += deltaRadians;
// Save the rotation in the history and remove any excessive elements
rotationHistory.push({
time: currentTime,
deltaRad: deltaRadians
});
while (rotationHistory.length > ROTATION_HISTORY_MAX_LENGTH) {
rotationHistory.shift();
}
}
if (input.isDragging) {
input.lastMouseCoords = input.mouseCoords;
}
// Continue rotation of the released wheel
if (!input.isDragging
&& !input.lastMouseCoords
&& lastAnimationTime
&& !self.isSnapping) {
var delta = currentTime - lastAnimationTime;
if (self.velocity == 0) {
var meanVelocityOverTime = getMeanVelocity(rotationHistory);
self.velocity = meanVelocityOverTime;
} else if (!self.isSnapping && !self.isDragging) {
var currentIsNegative = self.velocity < 0 ? true : false;
var currentVelocity = self.velocity;
if (currentIsNegative) {
currentVelocity /= -1;
}
var newVelocity = applyFakeFriction(currentVelocity,
delta);
if (newVelocity < 0) {
self.velocity = 0;
rotationHistory = [];
} else {
if (currentIsNegative) {
// Revert to old polarity
newVelocity /= -1;
}
self.velocity = newVelocity;
}
}
wheel.rotation.z += self.velocity * delta;
}
if (!self.isSnapping) {
while (wheel.rotation.z > 2 * Math.PI) {
wheel.rotation.z -= 2 * Math.PI;
}
while (wheel.rotation.z < - (2 * Math.PI)) {
wheel.rotation.z += 2 * Math.PI;
}
}
}
// Update snap tween
TWEEN.update();
// Update the history record
lastAnimationTime = currentTime;
}
function applyFakeFriction(velocity, time) {
/*
var currentRotation = wheel.rotation.z;
var nearestBorder = 0;
var nearestBorderDistance = 100;
for (var i = 0; i < PARTITIONS; i++) {
var partition = PARTITION_ARC * i - PARTITION_ARC * PARTITIONS / 2;
var distance = currentRotation - partition;
if (distance < 0) {
distance /= -1;
}
if (distance < nearestBorderDistance) {
console.log('distance is less than nearestBorderDistance')
nearestBorder = partition;
nearestBorderDistance = distance;
if (nearestBorderDistance < 0) {
nearestBorderDistance /= -1;
}
}
}
console.log('nearestBorderDistance: ', nearestBorderDistance);
*/
for (var i = 0; i < time; i++) {
velocity -= self.friction; // * (10000 * wheelSlowDownVelocityPositive); // * (nearestBorderDistance * BORDER_FRICTION);
}
return velocity;
}
function getNearestWedge() {
var currentRotation = wheel.rotation.z;
var nearestBorder = 0;
var nearestBorderDistance = 100;
for (var i = 0; i < PARTITIONS; i++) {
var partition = PARTITION_ARC * i - PARTITION_ARC * PARTITIONS / 2;
var distance = currentRotation - partition;
if (distance < 0) {
distance /= -1;
}
if (distance < nearestBorderDistance) {
console.log('distance is less than nearestBorderDistance')
nearestBorder = partition;
nearestBorderDistance = distance;
if (nearestBorderDistance < 0) {
nearestBorderDistance /= -1;
}
}
}
return {
position: nearestBorder,
distance: nearestBorderDistance
};
}
function snap() {
console.log('Snapping');
if (self.isSnapping) {
console.error('Already snapping, aborting.');
return;
}
self.isSnapping = true;
self.velocity = 0;
var nearest = getNearestWedge();
TWEEN.removeAll();
console.log('nearest: ', nearest.position, nearest.distance)
self.tween = new TWEEN.Tween({r: wheel.rotation.z})
.to({r: nearest.position})
.easing(TWEEN.Easing.Elastic.Out)
.onUpdate(onUpdate)
.onComplete(onComplete)
.start();
function onUpdate() {
//console.log('current: ', this.r, self.velocity);
wheel.rotation.z = this.r;
};
function onComplete() {
self.isSnapping = false;
console.log('Not snapping');;
}
}
function getMeanVelocity(history) {
if (history.length <= 1) {
return 0;
}
var movements = [];
var startTime = history[0].time;
var totalTimeElapsed = 0;
// Start from the second item in deltaRadians
for (var i = 1; i < history.length; i++) {
var item = history[i];
var movement = item.deltaRad;
movements.push(item.deltaRad);
var movementTimeDelta = item.time - startTime - totalTimeElapsed;
if (movementTimeDelta < 0) {
console.error('movementTimeDelta for history entry #' +
i + ' has travelled back in time somehow.');
}
totalTimeElapsed += movementTimeDelta;
}
var sum = movements.reduce(function (a, b) {
return a + b;
});
return sum / totalTimeElapsed;
}
// Internal utilities
function log() {
if (console && _log) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift('Animate: ')
console.log.apply(console, args);
}
}
// exports
this.update = update;
this.rotationHistory = rotationHistory;
this.PARTITIONS = PARTITIONS;
this.PARTITION_ARC = PARTITION_ARC;
this.snap = snap;
return this;
}
/**
* Handles input to the wheel.
*/
function Input() {
var self = this;
var _log = true;
domElement.addEventListener('mousedown', onDragStart, false);
//domElement.addEventListener('touchstart', onDragStart, false);
domElement.addEventListener('mouseup', onDragEnd, false);
domElement.addEventListener('mouseout', onDragEnd, false);
//domElement.addEventListener('touchend', onDragEnd, false);
domElement.addEventListener('mousemove', onMouseMove, false);
//domElement.addEventListener('touchmove', onMouseMove, false);
function onDragStart(e) {
self.isDragging = true;
log('Drag start');
self.mouseCoords = pageCoordsToCanvasCoords(e);
animate.rotationHistory = [];
}
function onDragEnd(e) {
self.isDragging = false;
self.lastMouseCoords = null;
self.mouseCoords = null;
log('Drag end');
}
function onMouseMove(e) {
e.preventDefault();
self.mouseCoords = pageCoordsToCanvasCoords(e);
}
function pageCoordsToCanvasCoords(e) {
var canvasX, canvasY;
if ('touches' in e && e.touches.length > 0) {
canvasX = e.touches[0].pageX;
canvasY = e.touches[0].pageY;
} else {
canvasX = e.pageX
canvasY = e.pageY
}
canvasX -= e.target.offsetLeft;
canvasY -= e.target.offsetTop;
// console.log(canvasX, canvasY);
return {
x: canvasX,
y: canvasY
};
}
function mouseCoordsToRadian(x, y) {
var origoX = WIDTH / 2.0,
origoY = HEIGHT / 2.0;
x = x - origoX;
y = y - origoY;
var atan = Math.atan2(x, y);
return atan;
}
// exports
this.mouseCoordsToRadian = mouseCoordsToRadian;
function log() {
if (console && _log) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift('Input: ')
console.log.apply(console, args);
}
}
return this;
}
// Internal utils
function log() {
if (console && _log) {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift('Wheel: ')
console.log.apply(console, args);
}
}
// exports
self.start = start;
self.update = update;
self.scene = scene;
self.camera = camera;
self.wheel = wheel;
self.renderer = renderer;
self.stats = stats;
self.domElement = domElement;
self.input = input;
self.animate = animate;
return self;
}
Excanvas does not work in IE8!!! I write program to paint by mouse on canvas element. There are fragment of my js-code
window.attachEvent('onload', function () {
function init() {
var w = document.getElementById('signatureImage').getAttribute('width');
var h = document.getElementById('signatureImage').getAttribute('height');
var removeSignatureImage = document.getElementById('signatureImage');
removeSignatureImage.parentNode.removeChild(removeSignatureImage);
var canvasDiv = document.getElementById('canvasDiv');
canvas = document.createElement('canvas');
canvas.setAttribute('width', w);
canvas.setAttribute('height', h);
canvas.setAttribute('style', 'border:1px solid #000000');
canvas.setAttribute('id', 'signatureImage');
canvasDiv.appendChild(canvas);
if (typeof G_vmlCanvasManager != 'undefined') {
canvas = window.G_vmlCanvasManager.initElement(canvas);
}
context = canvas.getContext("2d");
tool = new tool_pencil();
// Attach the mousedown, mousemove and mouseup event listeners.
var trackend = false;
var trackstart = false;
var trackmid = false;
canvas.onselectstart = function () {
canvas.onmousemove(); trackstart = true;
return false; }
canvas.onclick = function () { trackend = true; }
canvas.onmousemove = function () {
var mtarget = document.getElementById('signatureImage');
var x = event.clientX - canvas.offsetLeft;
var y = event.clientY - canvas.offsetTop;
var mtype = 'mousemove';
if (trackstart) {
trackstart = false;
trackmid = true;
mtype = 'mousedown';
}
if (trackend) {
trackmid = false;
mtype = 'mouseup';
}
if (trackend || trackmid || trackstart) {
trackend = false;
ev_canvas({
type: mtype,
_x: x,
_y: y,
target: mtarget
});
}
}
}
function tool_pencil() {
var tool = this;
this.started = false;
function getCoord(ev) {
var x = ev._x;
var y = ev._y;
if (tool.started == true) {
coords += x + "," + y + " ";
}
return [x, y];
}
this.mousedown = function (ev) {
context.beginPath();
context.moveTo(ev._x, ev._y);
tool.started = true;
};
this.mousemove = function (ev) {
if (tool.started) {
context.lineTo(ev._x, ev._y);
context.stroke();
var coord = getCoord(ev);
}
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
coordList += coords + ";";
document.getElementById('coord').value = coordList;
coords = "";
}
};
}
When there is context.lineTo(ev._x, ev._y);
context.stroke(); - nothing happens! Although the coordinates are passed and canvas painted and initialized