I have a page on my(very very recent only 2days old) website that contains a 3D model. The model was made in Blender, and was exported in the .glb format using glTF-Blender-IO by the KhronosGroup. It is uploaded with the THREE.GLTFLoader. Everything works perfectly fine on desktop and on iOS devices, however not on Android devices. The model simply does not show up. The error I get reads as follows:
Three.WebGlShader: Shader couldn't compile
Three.WebGlProgram: shader error:1282 35715 false
gl.getProgramInfoLog invalid shaders WARNING:0:1:'GL_OES_standard_derivatives':extension is not supported
'use strict';
window.onload = function() {
main();
}
function main() {
var canvas = document.querySelector('#canvasExpo58');
var renderer = new THREE.WebGLRenderer({canvas, antialias:true, alpha:true});
var fov = 45;
var aspect = window.innerWidth/window.innerHeight;
var near = 1;
var far = 1000;
var camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(10, 2, 5); //Zou moeten de initiƫle positie van de camera zijn bij opstarten: dus wat je als eerste ziet vooraleer je de muis beweegt
var controls = new THREE.OrbitControls(camera, canvas);
controls.target.set(10, 25, 10);
controls.update();
var scene = new THREE.Scene();
scene.background = renderer.setClearColor(0x000000, 0); //Deze geeft, samen met de alpha:true in de WebGLRenderer, een transparante achtergrond
{
var light1 = new THREE.DirectionalLight(new THREE.Color(0xFFFFFF), 0.8);
light1.position.set(-5, -5, 25);
scene.add(light1);
scene.add(light1.target);
}
{
var light2 = new THREE.DirectionalLight(new THREE.Color(0xFFFFFF), 1);
light2.position.set(-40, -10, 4);
scene.add(light2);
scene.add(light2.target);
}
{
var light3 = new THREE.AmbientLight(new THREE.Color(0xCBC9D3), 0.9);
scene.add(light3);
scene.add(light3.target);
}
{
var loader = new THREE.GLTFLoader();
loader.load(
'images/Dressoir.glb',
(gltf) => {
var root = gltf.scene;
scene.add(root);
var box = new THREE.Box3().setFromObject(root);
var boxSize = box.getSize(new THREE.Vector3()).length();
var boxCenter = box.getCenter(new THREE.Vector3());
//frameArea(boxSize * 0.5, boxSize, boxCenter, camera);
controls.maxDistance = boxSize * 10;
controls.target.copy(boxCenter);
controls.update();
}
);
}
function resizeRendererToDisplaySize(renderer) {
var canvas = renderer.domElement;
var width = canvas.clientWidth;
var height = canvas.clientHeight;
var needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
var canvas = renderer.domElement;
camera.aspect = canvas.clientWidth/canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
Webpage of the Live Server
I have tried searching the web for an answer, but found nothing relevant.
Related
I'm new to Angular-Ionic and I created my first App with THREE js, now. When I run it in Firefox or Chrome, I don't get any erros at all. But when I run it with Android Studio I get a lot of errors.
Examples:
Could not create a WebGL context, VENDOR = 0xffff, DEVICE = 0xffff, GL_VENDOR = Google (NVIDIA Corporation), GL_RENDERER = Android Emulator OpenGL ES Translator (NVIDIA GeForce MX350/PCIe/SSE2), GL_VERSION = 526.98, Sandboxed = no, Optimus = no, AMD switchable = no, Reset notification strategy = 0x8252, ErrorMessage = bindToCurrentThread failed:
glDrawElements: framebuffer incomplete (check)
Shader Error 1280 - VALIDATE_STATUS false
Failed to create a WebGL2 context.
in Angular Studio
in Firefox
my home.page.ts (and I know, it's not a clean code):
import { Component, ElementRef, HostListener, ViewChild } from '#angular/core';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { Water } from 'three/examples/jsm/objects/Water';
import { Sky } from 'three/examples/jsm/objects/Sky';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
steps = 1000
target = 10000
progress = this.steps / this.target
#ViewChild('canvas') container!: ElementRef;
protected scene
protected camera
protected renderer
protected controls
protected pmremGenerator
constructor() {
this.scene = new THREE.Scene()
this.camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 1, 20000);
this.renderer = new THREE.WebGLRenderer({antialias: true})
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.pmremGenerator = new THREE.PMREMGenerator(this.renderer)
}
ngAfterViewInit() {
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
this.container.nativeElement.appendChild(this.renderer.domElement);
this.camera.position.set(0, 0, -1000);
this.renderer.shadowMap.enabled = true;
this.controls.maxPolarAngle = Math.PI * 0.46;
this.controls.minDistance = 10.0;
this.controls.maxDistance = 200.0;
this.controls.update();
this.controls.update();
let sun = new THREE.Vector3();
const waterGeometry = new THREE.PlaneGeometry(10000, 10000);
let water = new Water(
waterGeometry,
{
textureWidth: 1024,
textureHeight: 1024,
waterNormals: new THREE.TextureLoader().load('../../assets/textures/waternormals.jpg', function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
}),
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 3.7,
fog: true
}
);
water.rotation.x = - Math.PI / 2;
this.scene.add(water);
const sky = new Sky();
sky.scale.setScalar(10000);
this.scene.add(sky);
const skyUniforms = sky.material.uniforms;
skyUniforms['turbidity'].value = 5;
skyUniforms['rayleigh'].value = 0.1;
skyUniforms['mieCoefficient'].value = 0.006;
skyUniforms['mieDirectionalG'].value = 0.55;
let renderTarget;
let cylinderGeo = new THREE.CylinderGeometry(50, 50, 10, 100)
let cylinderMat = new THREE.MeshStandardMaterial({
metalness: 0,
roughness: 1
})
let cylinder = new THREE.Mesh(cylinderGeo, cylinderMat)
this.scene.add(cylinder)
cylinder.position.set(0, -3, 0)
cylinder.receiveShadow = true
const loader = new GLTFLoader;
loader.load(
'assets/Wohnhaus.glb',
(object) => {
this.scene.add(object.scene);
object.scene.position.set(0, 2, 0)
object.scene.scale.set(10,10,10)
}
)
this.updateSun(sky, water, sun, renderTarget);
this.animate(water)
}
animate(water: Water) {
requestAnimationFrame(() => { this.animate(water) })
this.controls.update()
water.material.uniforms['time'].value += 1.0 / 60.0;
this.renderer.render(this.scene, this.camera)
}
#HostListener('window:resize', ['$event'])
onResize() {
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.render(this.scene, this.camera);
}
updateSun(sky: Sky, water: Water, sun: THREE.Vector3, renderTarget: any) {
const parameters = {
elevation: 10,
azimuth: 90
};
const phi = THREE.MathUtils.degToRad(90 - parameters.elevation);
const theta = THREE.MathUtils.degToRad(parameters.azimuth);
sun.setFromSphericalCoords(1, phi, theta);
sky.material.uniforms['sunPosition'].value.copy(sun);
water.material.uniforms['sunDirection'].value.copy(sun).normalize();
sky.castShadow = true
water.castShadow = true
if (renderTarget !== undefined) renderTarget.dispose();
renderTarget = this.pmremGenerator.fromScene(this.scene);
this.scene.environment = renderTarget.texture;
}
}
I already tried different Android Versions and Phones and I always have the same problems.
Update
I have changed WEBGLRenderer to WEBGL1Renderer. In my Code I removed the commented code. Now I don't get any Errors at all. I can see the cylinder now, but not the GLTFModel.
let cylinderGeo = new THREE.CylinderGeometry(50, 50, 10, 100)
/*let cylinderMat = new THREE.MeshStandardMaterial({
metalness: 0,
roughness: 1
})*/
let cylinder = new THREE.Mesh(cylinderGeo)
this.scene.add(cylinder)
cylinder.position.set(0, -3, 0)
const loader = new GLTFLoader;
loader.load(
'assets/Wohnhaus.glb',
(object) => {
this.scene.add(object.scene);
object.scene.position.set(0, 2, 0)
object.scene.scale.set(10, 10, 10)
}
)
I have a problem with radio stream spectrum analyzer in iOS\Android.
My HTML:
<canvas id="canvas" width="512" height="100"></canvas>
<div id="playStream">Play</div>
<div id="stopStream">Stop</div>
And my javascript code:
window.onload = function () {
var ctx = document.getElementById("canvas").getContext("2d");
var audioContext = new(window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext)();
var analyser = audioContext.createAnalyser();
analyser.fftSize = 512;
analyser.connect(audioContext.destination);
var frequencyBins = new Uint8Array(analyser.frequencyBinCount);
var filter = audioContext.createBiquadFilter();
filter.type = "highpass";
filter.frequency.value = 0.0001;
filter.connect(analyser);
window.audio = new Audio('http://noasrv.caster.fm:10099/128mp3_autodj');
audio.crossOrigin = 'anonymous';
var audioSrc = audioContext.createMediaElementSource(audio);
audioSrc.connect(analyser);
audioSrc.connect(filter);
var osc = audioContext.createOscillator();
osc.connect(filter);
var WIDTH = 512;
var HEIGHT = 100;
var value, h, w;
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i = 0; i < frequencyBins.length; i++) {
value = frequencyBins[i];
h = HEIGHT * (value / 255);
w = WIDTH / frequencyBins.length;
ctx.fillRect(i * w, HEIGHT - 1, w, -h);
}
};
function animate() {
analyser.getByteFrequencyData(frequencyBins);
//console.log(frequencyBins);
draw();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
playStream.addEventListener('click', function(){
audio.play();
}, false)
stopStream.addEventListener('click', function(){
audio.pause();
}, false)
};
And here is an example: http://jsfiddle.net/a2ZL9/46/.
This code works fine in browsers, but when I build android app it's doesn't work. Audio playing, but there is no animation of spectrum analyzer.
Can somebody help me with this problem?
I have tried various solutions: $cordovaCapture, $cordovaCamera(DATA_URL can display the picture, but i want file_URI to do the same).
here is my code snippet:
$scope.addImage = function() {
var options = {limit: 1};
$cordovaCapture.captureImage(options).then(function(imageData) {
console.log(imageData);
// var jsonobj=angular.toJson(imageData);
$scope.profile.image = imageData[0];
console.log(angular.toJson(imageData));
console.log($scope.profile.image.localURL);//the path to upload
document.getElementById('myImage').src = "'"+$scope.profile.image.localURL+"'";//have already tried without the quottes
/* window.plugins.Base64.encodeFile($scope.profile.image.localURL,function(base64){ // Encode URI to Base64 needed for contacts plugin
$scope.profile.image.preview = base64;
console.log($scope.profile.image.preview);
});*/
// Success! Image data is here
}, function(err) {
});
i even tried whitelisting certainties in the module as in :
.config( [
'$compileProvider',
function( $compileProvider )
{
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|file|content|blob|cdvfile):|data:image\//);
}
])
It didn't help either. I am testing the project in both device and emulator. I even tried base64 encoding the file from the path. Nothing works as to display the recently taken picture. The path that i retrieve is like this:
cdvfile://localhost/persistent/DCIM/Camera/123123123.jpg
Instead of using file_URI to upload the image. I used data_URL,converted the image to blob and used the cordova-file-transfer plugin to upload the file to the server. In that way, i could use the base64 encoded image on the html side as well as upload at the same time.
$scope.captureImage = function() {
navigator.camera.getPicture(cameraSuccess, cameraError, {
destinationType: Camera.DestinationType.DATA_URL,
correctOrientation: true
});
}
var cameraSuccess = function(imageData) {
$scope.profileImageSource = imageData;
$scope.changeImage = function(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 512;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, {
type: contentType
});
}
$scope.picture = $scope.changeImage(imageData, 'image/png');
$scope.$digest();
}
HTML:
<img ng-src="data:image/gif;base64,{{profileImageSource}}">
I am relatively new to d3js, and trying to create a visualization of my quartet's concert schedule using a map. My first attempt works great in a desktop chrome browser and desktop safari browser.
On my android device in mobile chrome, the map renders the entire globe incorrectly except for the United States.
http://test.chiaraquartet.net/topo/index.html
Any insight into what I am doing wrong/if there is a bug in d3 would be appreciated.
Here is the code in question:
var center = [90, -38.7],
ratio = window.devicePixelRatio || 1,
graticule = d3.geo.graticule(),
width = 500,
height = 500,
degrees = 180 / Math.PI,
projection = d3.geo.orthographic()
.scale(height / 2 - 1)
.rotate(center)
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.1)
var graticule = d3.geo.graticule()()
// Round to integer pixels for speed, and set pixel ratio.
function roundRatioContext(context) {
return {
moveTo: function(x, y) { context.moveTo(Math.round(x * ratio), Math.round(y * ratio)); },
lineTo: function(x, y) { context.lineTo(Math.round(x * ratio), Math.round(y * ratio)); },
closePath: function() { context.closePath(); }
};
}
var canvas = d3.select("body").append("canvas")
.attr("width", width * ratio)
.attr("height", height * ratio)
.style("width", width + "px")
.style("height", height + "px")
var c = canvas.node().getContext("2d");
var path = d3.geo.path()
.projection(projection)
.context(roundRatioContext(c));
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append('g')
var textbox = d3.select("body").append("div")
.style('position', 'absolute')
.attr('class', 'popup')
.classed('hidden', true)
var sphere = svg.append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path)
var runner = queue()
.defer(d3.json, 'world.json')
.defer(d3.xhr('/concertrpc.php')
.header('content-type', 'application/json')
.post, JSON.stringify({
params: [],
id: 1
}))
.await(function(error, world, info) {
if (error) return console.error(error);
var features = topojson.feature(world, world.objects.states)
var countries = topojson.feature(world, world.objects.countries)
console.log(world)
var concerts = JSON.parse(info.responseText)
if (concerts.error) {
console.log(concerts.error)
return
}
concerts = concerts.result.concerts
console.log(concerts)
var globe = {type: "Sphere"},
graticule = d3.geo.graticule()(),
countries = topojson.feature(world, world.objects.countries),
states = topojson.feature(world, world.objects.states),
country_borders = topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b }),
state_borders = topojson.mesh(world, world.objects.states, function(a, b) { return a.id !== b.id }),
temp_context
temp_context = path.context()
path.context(null)
svg.selectAll('.concert')
.data(concerts, function(d) { return d.properties.id })
.enter().append("path")
.attr('class', 'concert')
.attr('d', path)
.on('mouseover', concertMouseover)
.on('mouseout', function(d) {
textbox.classed('hidden', true)
})
path.context(temp_context)
d3.select('body').append('div').append('button')
.attr('type', 'button')
.text('This season')
.on('click', function() {
getConcertData('This season')
})
var zoom = d3.geo.zoom()
.projection(projection)
.duration(function(S) { return 2000 * Math.sqrt(S); }) // assume ease="quad-in-out"
.scaleExtent([height / 2 - 1, Infinity])
.on("zoom", function() {
projection.clipAngle(Math.asin(Math.min(1, .5 * Math.sqrt(width * width + height * height) / projection.scale())) * degrees);
c.clearRect(0, 0, width * ratio, height * ratio);
c.strokeStyle = "#999", c.lineWidth = .25 * ratio, c.beginPath(), path(graticule), c.stroke();
c.fillStyle = "#69d2e7", c.beginPath(), path(countries), c.fill();
c.fillStyle = "#00f", c.beginPath(), path(states), c.fill();
c.strokeStyle = "#fff", c.lineWidth = .5 * ratio, c.beginPath(), path(country_borders), c.stroke();
c.strokeStyle = "#fff", c.lineWidth = .5 * ratio, c.beginPath(), path(state_borders), c.stroke();
c.strokeStyle = "#000", c.lineWidth = .5 * ratio, c.beginPath(), path(globe), c.stroke();
temp_context = path.context()
path.context(null)
svg.selectAll("path").attr("d",path);
path.context(temp_context)
})
//.on("zoomend", transition);
canvas
.call(zoom)
.call(zoom.event);
sphere
.call(zoom)
function transition() {
zoomBounds(projection, states.features[30]);
canvas.transition()
.ease("quad-in-out")
.duration(2000) // see https://github.com/mbostock/d3/pull/2045
.call(zoom.projection(projection).event);
}
function zoomBounds(projection, o) {
var centroid = d3.geo.centroid(o),
clip = projection.clipExtent();
projection
.rotate([-centroid[0], -centroid[1]])
.clipExtent(null)
.scale(1)
.translate([0, 0]);
var b = path.bounds(o),
k = Math.min(1000, .45 / Math.max(Math.max(Math.abs(b[1][0]), Math.abs(b[0][0])) / width, Math.max(Math.abs(b[1][1]), Math.abs(b[0][1])) / height));
projection
.clipExtent(clip)
.scale(k)
.translate([width / 2, height / 2]);
}
})
function concertMouseover(d) {
var loc = projection(d3.select(this).datum().geometry.coordinates)
textbox.style('top', loc[1] + "px")
textbox.style('left', loc[0] + 15 + "px")
textbox.text(d.properties.title)
textbox.classed('hidden', false)
}
function getConcertData(request) {
d3.xhr('/concertrpc.php')
.header('content-type', 'application/json')
.post(JSON.stringify({
params: [request],
id: 1
}), function(error, info) {
var concerts = JSON.parse(info.responseText)
if (concerts.error) {
console.log(concerts.error)
return
}
var c = svg.selectAll('.concert')
.data(concerts.result.concerts, function(d) { return d.properties.id })
c.transition()
.style('opacity', '1')
c.enter()
.append('path')
.attr('class', 'concert')
.attr('d', path)
.on('mouseover', concertMouseover)
.on('mouseout', function(d) {
textbox.classed('hidden', true)
})
c.exit().transition()
.duration(1000)
.style('opacity', '0')
.remove()
})
}
I'm not sure what was causing the problem, but the culprit was in the topojson file generated by my attempts to combine a map of the countries of the world with a map of the states of the USA. I found a different map of the world to use, and now the map displays the same way on both the phone and the desktop.
I am doing project in android phonegap application using kineticjs and html5
I got 3 errors don't know what is the exact problem
Uncaught typeError cannot call method 'remove children' of undefined at file:///android_assest/www/index.html
Uncaught typeError cannot read property 'children' of undefined at file:///android_assest/www/index.html
Uncaught referenceError kinetic is not defined at file:///android_assest/www/index.html
I google it but cudn't find the answer so any help would be greatful
function clearCanvas() {
layer.removeChildren();
layer.draw();
haveBackground = false;
}
function downloadCanvas() {
var canvas = stage.children[0].canvas;
var oImgPNG = Canvas2Image.saveAsPNG(canvas, true);
document.body.appendChild(oImgPNG);
}
<img alt="" src="res/drawable-hdpi/design15.png" id="gabby" onClick="addClickedImage('gabby ')"/><br />
above those snippets in which i am getting error.Thanks in advance
Here is the script of my programm
<script type="text/javascript" src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.1.js"></script>
<script type="text/javascript" src="assets/www/base64.js"></script>
<script type="text/javascript" src="assets/www/canvas2image.js"></script>
<script type="text/javascript">
debugger;
var stage;
var layer;
var selected;
var wasSelected;
var haveBackground; // first dragged image sets stage size
/*
* Set up canvas stage and layer
*/
function initCanvas(id) {
stage = new Kinetic.Stage({
container: id,
width: 150,
height: 50
});
layer = new Kinetic.Layer();
stage.add(layer);
stage.draw();
}
/*
* Clear canvas, start again
*/
function clearCanvas() {
layer.removeChildren();
layer.draw();
haveBackground = false;
}
/*
* Download canvas
*/
function downloadCanvas() {
var canvas = stage.children[0].canvas;
var oImgPNG = Canvas2Image.saveAsPNG(canvas, true);
document.body.appendChild(oImgPNG);
}
/*
* Upload File
*/
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$('#blah')
.attr('src', e.target.result)
.width(20)
.height(20);
};
reader.readAsDataURL(input.files[0]);
}
}
/*
* Resize
*/
function resize(group, activeAnchor) {
var tl = group.get(".tl")[0];
var tr = group.get(".tr")[0]; group
var br = group.get(".br")[0];
var bl = group.get(".bl")[0];
var handle = group.get(".handle")[0];
var ghost = group.get(".ghost")[0];
var flip = group.get(".flip")[0];
var image = group.get(".image")[0];
switch (activeAnchor.attrs.name) {
case "tl":
bl.setPosition(tl.attrs.x, br.attrs.y);
tr.setPosition(br.attrs.x, tl.attrs.y);
break;
case "tr":
br.setPosition(tr.attrs.x, bl.attrs.y);
tl.setPosition(bl.attrs.x, tr.attrs.y);
break;
case "bl":
br.setPosition(tr.attrs.x, bl.attrs.y);
tl.setPosition(bl.attrs.x, tr.attrs.y);
break;
case "br":
bl.setPosition(tl.attrs.x, br.attrs.y);
tr.setPosition(br.attrs.x, tl.attrs.y);
break;
}
handle.setPosition((tr.attrs.x + tl.attrs.x) / 2, tl.attrs.y - 20);
ghost.setPosition(handle.getPosition());
flip.setPosition((tr.attrs.x + tl.attrs.x) / 2, bl.attrs.y + 20);
image.setPosition(tl.attrs.x, tl.attrs.y);
image.attrs.width = tr.attrs.x - tl.attrs.x;
image.attrs.height = bl.attrs.y - tl.attrs.y;
}
/*
* Rotate
*/
function rotate(group) {
var c = group.getAbsolutePosition();
var p0 = { x: c.x, y: c.y - 50 };
var p1 = stage.getUserPosition();
var p0c = Math.sqrt(Math.pow(c.x - p0.x, 2) + Math.pow(c.y - p0.y, 2)); // p0->c (b)
var p1c = Math.sqrt(Math.pow(c.x - p1.x, 2) + Math.pow(c.y - p1.y, 2)); // p1->c (a)
var p0p1 = Math.sqrt(Math.pow(p1.x - p0.x, 2) + Math.pow(p1.y - p0.y, 2)); // p0->p1 (c)
var deg = Math.acos((p1c * p1c + p0c * p0c - p0p1 * p0p1) / (2 * p1c * p0c));
// fix for negative rotation
if (p1.x < c.x) {
deg = (360 * (Math.PI / 180)) - deg;
}
group.setRotation(deg);
}
/*
* Flip
*/
function flip(group) {
group.attrs.scale.x = group.attrs.scale.x * -1;
}
/*
* Fix center offset (due to resizing from a corner)
*/
function fixCenterOffset(group) {
var image = group.get(".image")[0]
var currentOffset = group.getCenterOffset();
var currentPosition = group.getPosition();
var newOffset = { x: image.attrs.width / 2, y: image.attrs.height / 2 };
var newPosition = {
x: currentPosition.x - (currentOffset.x - newOffset.x),
y: currentPosition.y - (currentOffset.y - newOffset.y)
}
group.setCenterOffset(newOffset);
group.setPosition(newPosition);
layer.draw();
}
/*
* Prepare background image
*/
function prepareBackground(img) {
var maxWidth = 200;
var maxHeight = 100;
if (img.width > maxWidth) {
img.height = (maxWidth / img.width) * img.height;
img.width = maxWidth;
}
if (img.height > maxHeight) {
img.width = (maxHeight / img.height) * img.width;
img.height = maxHeight;
}
return img;
}
/*
* Add an image plus anchors to the canvas using group
*/
function initImage(img, type) {
if (type == "background") {
var kimggroup = initBackgroundImage(img);
} else {
var kimggroup = initNormalImage(img);
}
layer.add(kimggroup);
stage.add(layer);
// Draw the img
stage.draw();
}
/*
* Add background image
*/
function initBackgroundImage(img) {
var img = prepareBackground(img);
stage.setSize(img.width, img.height);
var kimggroup = new Kinetic.Group({
x: 0,
y: 0,
draggable: false
});
// Make the img and add it to the group
var kimg = new Kinetic.Image({
x: 0,
y: 0,
image: img,
width: img.width,
height: img.height,
name: "image"
});
kimggroup.add(kimg);
return kimggroup;
}
/*
* Add normal image
*/
function initNormalImage(img) {
var kimggroup = new Kinetic.Group({
x: stage.attrs.width / 2,
y: stage.attrs.height / 2,
draggable: true,
centerOffset: [img.width / 2, img.height / 2]
});
// Make the img and add it to the group
var kimg = new Kinetic.Image({
x: 0,
y: 0,
image: img,
width: img.width,
height: img.height,
name: "image"
});
kimggroup.add(kimg);
// Add anchors for resizing and rotation
addAnchor(kimggroup, 0, 0, "tl");
addAnchor(kimggroup, img.width, 0, "tr");
addAnchor(kimggroup, img.width, img.height, "br");
addAnchor(kimggroup, 0, img.height, "bl");
addAnchor(kimggroup, img.width / 2, -20, "handle");
addAnchor(kimggroup, img.width / 2, -20, "ghost");
addAnchor(kimggroup, img.width / 2, img.height + 20, "flip");
// On click make the image selected
kimggroup.on("mousedown", function () {
wasSelected = selected;
selected = this;
updateSelected();
this.moveToTop();
stage.draw();
});
kimg.on("mouseover", function () {
document.body.style.cursor = "move";
});
kimg.on("mouseout", function () {
document.body.style.cursor = "default";
});
// Double click to remove
kimg.on("dblclick dbltap", function () {
layer.remove(kimggroup);
layer.draw();
});
return kimggroup;
}
/*
* Create anchor and add to group
*/
function addAnchor(group, x, y, name) {
var config = {
x: x,
y: y,
stroke: "#666",
fill: "#ddd",
strokeWidth: 2,
radius: 4,
name: name,
draggable: true
}
switch (name) {
case "handle":
config.draggable = false;
var anchor = addRotateAnchor(group, config);
break;
case "ghost":
config.stroke = "#666";
var anchor = addRotateGhostAnchor(group, config);
break;
case "flip":
config.stroke = "#666";
config.draggable = false;
var anchor = addFlipAnchor(group, config);
break;
default:
var anchor = addResizeAnchor(group, config);
break;
}
anchor.hide();
group.add(anchor);
}
/*
* Set up resize anchor
*/
function addResizeAnchor(group, config) {
var anchor = new Kinetic.Circle(config);
anchor.on("dragmove", function () {
resize(group, this);
layer.draw();
});
anchor.on("mousedown touchstart", function () {
group.draggable(false);
this.moveToTop();
});
anchor.on("dragend", function () {
fixCenterOffset(group);
group.draggable(true);
layer.draw();
});
anchor.on("mouseover", function () {
var layer = this.getLayer();
document.body.style.cursor = "pointer";
this.setStrokeWidth(3);
layer.draw();
});
anchor.on("mouseout", function () {
var layer = this.getLayer();
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});
return anchor;
}
/*
* Set up rotation anchor
*/
function addRotateAnchor(group, config) {
var anchor = new Kinetic.Circle(config);
anchor.on("mouseover", function () {
var layer = this.getLayer();
document.body.style.cursor = "pointer";
this.setStrokeWidth(3);
layer.draw();
});
anchor.on("mouseout", function () {
var layer = this.getLayer();
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});
return anchor;
}
/*
* Set up rotation ghost anchor
*/
function addRotateGhostAnchor(group, config) {
var anchor = new Kinetic.Circle(config);
anchor.on("dragmove", function () {
rotate(group);
layer.draw();
});
anchor.on("mousedown touchstart", function () {
group.draggable(false);
this.moveToTop();
});
anchor.on("dragend", function () {
var handle = group.get(".handle")[0];
this.setPosition(handle.getPosition());
group.draggable(true);
layer.draw();
});
anchor.on("mouseover", function () {
var layer = this.getLayer();
document.body.style.cursor = "pointer";
this.setStrokeWidth(3);
layer.draw();
});
anchor.on("mouseout", function () {
var layer = this.getLayer();
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});
return anchor;
}
/*
* Set up flip anchor
*/
function addFlipAnchor(group, config) {
var anchor = new Kinetic.Circle(config);
anchor.on("mousedown touchstart", function () {
flip(group);
layer.draw();
});
anchor.on("mouseover", function () {
var layer = this.getLayer();
document.body.style.cursor = "pointer";
this.setStrokeWidth(3);
layer.draw();
});
anchor.on("mouseout", function () {
var layer = this.getLayer();
document.body.style.cursor = "default";
this.setStrokeWidth(2);
layer.draw();
});
return anchor;
}
/*
* Show anchors only when group selected
*/
function updateSelected() {
// Deselect the old img if there was any
if (wasSelected) {
wasSelected.get(".tl")[0].hide();
wasSelected.get(".tr")[0].hide();
wasSelected.get(".br")[0].hide();
wasSelected.get(".bl")[0].hide();
wasSelected.get(".handle")[0].hide();
wasSelected.get(".ghost")[0].hide();
wasSelected.get(".flip")[0].hide();
}
// Select the new image
selected.get(".tl")[0].show();
selected.get(".tr")[0].show();
selected.get(".br")[0].show();
selected.get(".bl")[0].show();
selected.get(".handle")[0].show();
selected.get(".ghost")[0].show();
selected.get(".flip")[0].show();
}
/*
* Add clicked images to the canvas
*/
function addClickedImage(name) {
var img = document.getElementById(name);
initImage(img, "normal");
}
/*
* Listen for images dragged into canvas and add them
*/
function setupDragAndDrop() {
stage.content.addEventListener("dragover", function (evt) {
evt.preventDefault();
}, false);
// Handle dropped image file - only Firefox and Google Chrome
stage.content.addEventListener("drop", function (evt) {
dragImg = new Image();
var files = evt.dataTransfer.files;
if (files.length > 0) {
var file = files[0];
if (typeof FileReader !== "undefined" && file.type.indexOf("image") != -1) {
var reader = new FileReader();
reader.onload = function (evt) {
dragImg.src = evt.target.result;
};
reader.readAsDataURL(file);
dragImg.onload = function () {
if (!haveBackground) {
haveBackground = true;
var type = "background";
} else {
var type = "normal";
}
initImage(this, type);
}
}
}
evt.preventDefault();
}, false);
}
window.onload = function () {
initCanvas("container");
setupDragAndDrop();
};
</script>
More details:
since you cannot 'remove children' of undefined, your layer is not being defined somewhere (or is removed or overwritten).
Make sure you have this somewhere:
var layer = new Kinetic.Layer();
not being able to access children means the same for your stage. but simply put since 'kinetic' is not defined somewhere you are either not including the Kinetic.4.3.0.1.JS in your sources, or you are declaring it incorrectly.
Make sure you have the kinetic.js file referenced somewhere, like in either the or tags:
<script src='http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.1.js'></script>
or link to a hard copy in your project
<script src='../kinetic-v4.3.1.js'></script> // in this example it is located one directory up, but you could put it anywhere really.
It's scoping issue.
Your variable layer is not accessible from function clearCanvas().
Not ideal, but it works by defining the variable first, then use it later like the following;
<script>
var layer;
$( function() {
layer = new Kinetic.Layer();
.....
});
</script>
so does the same apply to your variable 'stage'
--- edited after your comment ---
<script>
var layer, script, stage;
function initCanvas(id) {
stage = new Kinetic.Stage({ container: id, width: 150, height: 50 });
layer = new Kinetic.Layer();
stage.add(layer);
stage.draw();
}
</script>
if you defined 'stage = something' inside function it works like 'var stage=something'.
so if you want to access stage and layer outside of function, you defined it outside of your function.