When the canvas that contains a createjs is zoomed using a zoom style (e.g. zoom: 0.5) buttons on the canvas have a different click target that is either on only one side of the button or no target at all.
var stage, label;
function init() {
stage = new createjs.Stage("demoCanvas");
stage.name = "stage";
var background = new createjs.Shape();
background.name = "background";
background.graphics.beginFill("red").drawRoundRect(0, 0, 150, 60, 10);
label = new createjs.Text("foo", "bold 24px Arial", "#FFFFFF");
label.name = "label";
label.textAlign = "center";
label.textBaseline = "middle";
label.x = 150/2;
label.y = 60/2;
var button = new createjs.Container();
button.name = "button";
button.x = 75;
button.y = 40;
button.addChild(background, label);
stage.addChild(button);
// listeners
var targets = [stage,button,label,background];
for (var i=0; i<targets.length; i++) {
var target = targets[i];
target.on("click", toggleText, null, false, null, false);
}
stage.update();
}
function toggleText(evt) {
label.text = (label.text == 'foo') ? 'bar' : 'foo';
stage.update();
}
body{
margin: 0;
text-align: center;
}
#demoCanvas{
background: tan;
width: 500px;
height: 300px;
transform-origin: 0 0;
transform: scale(0.2); // Firefox = This does not cause problem with button clicking
zoom: 0.2; // Other browsers = This causes a problem with button clicking in Chrome
}
<html>
<head>
<script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
</head>
<body onload="init();">
<canvas id="demoCanvas"></canvas>
</body>
</html>
Note that this will not fail in the SO snippet so I have also made this available as a Pen.
This behaviour presents itself in Chrome but not in Firefox (which recognises the transform: scale() style instead).
If the canvas needs to be scaled in the DOM how can this be done in Chrome without causing buttons to fail?
The zoom css style is considered non-standard so for Chrome and other Browsers you can use transform : scale() as well. I can confirm that zoom does break CreateJS - if you wanted to, you could add an issue to their EaselJS GitHub - not sure if it is worth fixing when transform : scale() can be used. Cheers.
CSS ZOOM: "Non-standard
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future."
Related
This may be more of a "you're going to have to do it the long way" type of deal, but here it goes...
When I apply CSS to control the opacity of only one HTML element that is the container of a Three.JS scene in where there are multiple elements that each are a container for their own scene, the CSS applied (even at an inline level) to only one of those elements containing a scene is being applied to all elements that contain a scene and not specifically the one targeted. This happens with any post applied CSS attribute, not just opacity.
The reason why I am attempting this approach at controlling opacity this way is that per my research there is no direct way to set an opacity on a Three.JS group object that contains 1 to N number of meshes. I am (in theory) trying not to have to define all materials with transparency set to "true" and then having to do recursive updates to all meshes in a Three.JS Group object where an animation would fade in/out.
Some of the group objects I'm working with will have many meshes defined in them. Thus, rather than update the opacity of each individual mesh itself contained within a Three.JS group object, my goal was/is to have individual scenes for each type of animation that is capable of having any amount of transparency applied to it run as is then just adjust the HTML element containing that animation's opacity property.
I've tried using one camera and multiple cameras to no avail. I've also tried nesting the containers in one additional element and setting CSS on the parent element but the same issue occurs. I have not tried using multiple renderers as from what I gather in research is that doing so is frowned upon and can lead to performance issues and context limits. The render loop also has "autoClear" set to false so that all scenes render together.
Here is the HTML syntax. You will notice that the first element has a inline style for opacity set to 0.5 and the second element has no inline styling applied:
<div class="three-js-container" id="scene-container-1" style="opacity:0.5;"></div>
<div class="three-js-container" id="scene-container-2"></div>
Here is the Javascript code:
/* Only one renderer instance is created */
var universalRenderer = new THREE.WebGLRenderer({antialias: true, alpha:true});
/* references to all containers are made */
var containerForScene1 = document.getElementById("scene-container-1");
var containerForScene2 = document.getElementById("scene-container-2");
/* two different cameras are created */
var cameraForScene1 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);
var cameraForScene2 = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.001, 1000);
/* two different scenes are created, one for each element container */
var scene1 = new THREE.Scene();
scene1.userData.element = containerForScene1;
var scene2 = new THREE.Scene();
scene2.userData.element = containerForScene2;
/* the renderer is applied to both scene containers */
containerForScene1.appendChild(universalRenderer.domElement);
containerForScene2.appendChild(universalRenderer.domElement);
When both animations are played, both scenes are rendered at 1/2 opacity rather than just the first scene itself.
Is there a reason why CSS styling applied to one HTML scene containing element is applied to all of the other scene containing elements? Will I just have to suck it up and go the long way around in controlling mesh opacity?
Thanks.
Setting transparency for a THREE.Group:
A Group is just a container. As such, it has children, which are potentially other Groups. But you can only apply transparency to a Material, which is assigned at the Mesh level, not Groups. However, not all is lost, because you can monkey patch Group to allow you to perform the operation seamlessly.
// MONKEY PATCH
Object.defineProperty(THREE.Group.prototype, "transparent", {
set: function(newXP) {
this.traverse(node => {
if (node.material) {
node.material.transparent = newXP
node.material.opacity = (newXP) ? 0.5 : 1
}
})
}
})
// Set up the renderer
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true
})
document.body.appendChild(renderer.domElement)
renderer.setSize(window.innerWidth, window.innerHeight)
const scene = new THREE.Scene()
const size = new THREE.Vector2()
renderer.getSize(size)
const camera = new THREE.PerspectiveCamera(28, size.x / size.y, 1, 1000)
camera.position.set(0, 20, 100)
camera.lookAt(scene.position)
scene.add(camera)
camera.add(new THREE.PointLight(0xffffff, 1))
function render() {
renderer.render(scene, camera)
}
const axis = new THREE.Vector3(0, 1, 0)
function animate() {
requestAnimationFrame(animate)
camera.position.applyAxisAngle(axis, 0.005)
camera.lookAt(scene.position)
render()
}
animate()
// Populate the scene
const cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5)
let opaqueCubes = []
let transparentCubes = []
const randomInRange = () => Math.random() * ((Math.random() <= 0.5) ? -10 : 10)
const opaqueGroup = new THREE.Group()
scene.add(opaqueGroup)
for (let i = 0; i < 10; ++i) {
opaqueGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
color: "red"
})))
opaqueGroup.children[i].position.set(randomInRange(), randomInRange(), randomInRange())
}
const transparentGroup = new THREE.Group()
scene.add(transparentGroup)
for (let i = 0; i < 10; ++i) {
transparentGroup.add(new THREE.Mesh(cubeGeo, new THREE.MeshPhongMaterial({
color: "green"
})))
transparentGroup.children[i].position.set(randomInRange(-10, 10), randomInRange(-10, 10), randomInRange(-10, 10))
}
// Control the transparency from the input
const xparent = document.getElementById("xparent")
xparent.addEventListener("change", (e) => {
transparentGroup.transparent = xparent.checked
})
html,
body {
padding: 0;
margin: 0;
overflow: hidden;
}
#control {
position: absolute;
top: 0;
left: 0;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/105/three.js"></script>
<div id="control">
<label>Make the green ones transparent:<input id="xparent" type="checkbox" /></label>
<div>
I wonder how can I use css/javascript to adjust the blinking cursor inside the search box with CSS?
Is it possible to replace default blinkig caret to horizontal blinking icon
I don't think it is so hard. I made a quick example, which works in most modern browsers except Safari.
It draws the caret on a canvas, and sets it as a background of the input, on a position calculated from the browsers caret position.
It checks if the browser supports the caret-color css property, and if it doesn't it doesn't do anything, because both the system caret, and our caret will be visible in the same time. From the browsers I tested, only Safari doesn't support it.
$("input").on('change blur mouseup focus keydown keyup', function(evt) {
var $el = $(evt.target);
//check if the carret can be hidden
//AFAIK from the modern mainstream browsers
//only Safari doesn't support caret-color
if (!$el.css("caret-color")) return;
var caretIndex = $el[0].selectionStart;
var textBeforeCarret = $el.val().substring(0, caretIndex);
var bgr = getBackgroundStyle($el, textBeforeCarret);
$el.css("background", bgr);
clearInterval(window.blinkInterval);
//just an examplethis should be in a module scope, not on window level
window.blinkInterval = setInterval(blink, 600);
})
function blink() {
$("input").each((index, el) => {
var $el = $(el);
if ($el.css("background-blend-mode") != "normal") {
$el.css("background-blend-mode", "normal");
} else {
$el.css("background-blend-mode", "color-burn");
}
});
}
function getBackgroundStyle($el, text) {
var fontSize = $el.css("font-size");
var fontFamily = $el.css("font-family");
var font = fontSize + " " + fontFamily;
var canvas = $el.data("carretCanvas");
//cache the canvas for performance reasons
//it is a good idea to invalidate if the input size changes because of the browser text resize/zoom)
if (canvas == null) {
canvas = document.createElement("canvas");
$el.data("carretCanvas", canvas);
var ctx = canvas.getContext("2d");
ctx.font = font;
ctx.strokeStyle = $el.css("color");
ctx.lineWidth = Math.ceil(parseInt(fontSize) / 5);
ctx.beginPath();
ctx.moveTo(0, 0);
//aproximate width of the caret
ctx.lineTo(parseInt(fontSize) / 2, 0);
ctx.stroke();
}
var offsetLeft = canvas.getContext("2d").measureText(text).width + parseInt($el.css("padding-left"));
return "#fff url(" + canvas.toDataURL() + ") no-repeat " +
(offsetLeft - $el.scrollLeft()) + "px " +
($el.height() + parseInt($el.css("padding-top"))) + "px";
}
input {
caret-color: transparent;
padding: 3px;
font-size: 15px;
color: #2795EE;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" />
If there is interest, I can clean it a bit and wrap it in a jQuery plugin.
Edit: forgot about the blinking, so I added it. A better way will be to add it as css animation, in this case the caret should be in a separate html element positioned over the input.
Changing the color of the caret is supported by the latest standards. But not changing its width is not, which I think is a shame because it is a question of accessibility for vision-impaired people.
One approach for implementing such a change yourself is first trying to figure out what is the position the caret is blinking at, then overlaying it with an element that looks like the caret but is perhaps wider etc.
Here's an article on how to go about doing such a thing. It's a good article but the end-solution is kind of complicated as a whole. But see if it solves your problem:
https://medium.com/#jh3y/how-to-where-s-the-caret-getting-the-xy-position-of-the-caret-a24ba372990a
Here is perhaps a simpler explanation for how to find the care x-y position:
How do I get the (x, y) pixel coordinates of the caret in text boxes?
I'm looking to do a slide-card style website with html/css/js.
I have seen some nice examples like:
http://www.thepetedesign.com/demos/onepage_scroll_demo.html
http://alvarotrigo.com/fullPage/
However, what these DON'T seem to do is slide a page out WHILE the page underneath is visible, as if they were a stack of index cards. Parallax scrolling does this, but it typically will wipe the existing area, rather then scroll/move it off screen. Any ideas?
Here is a fiddle using JQuery that does something like what you are looking for, you could implement it with that one scroll effect of the card sliders and have it animate in probably.
http://jsfiddle.net/d6rKn/
(function(window){ $.fn.stopAtTop= function () {
var $this = this,
$window = $(window),
thisPos = $this.offset().top,
setPosition,
under,
over;
under = function(){
if ($window.scrollTop() < thisPos) {
$this.css({
position: 'absolute',
top: ""
});
setPosition = over;
}
};
over = function(){
if (!($window.scrollTop() < thisPos)){
$this.css({
position: 'fixed',
top: 0
});
setPosition = under;
}
};
setPosition = over;
$window.resize(function()
{
bumperPos = pos.offset().top;
thisHeight = $this.outerHeight();
setPosition();
});
$window.scroll(function(){setPosition();});
setPosition();
};
})(window);
$('#one').stopAtTop();
$('#two').stopAtTop();
$('#three').stopAtTop();
$('#four').stopAtTop();
See the fiddle for HTML and CSS.
Not my fiddle just grabbed it with a quick google search.
I have two canvases. The first is supposed to be the background and it's content is rendered via raw WebGL (3D). The second canvas is supposed to overlay the first one and is mainly transparent. It's content is rendered via three.js (3D content). Unfortunately the second canvas is not drawn on top of the first one, but next to it.
How can I draw the second canvas above the first one with transparent three.js scene background?
Update:
I integrated gaitat's idea. However it's still not working (see Fullscreen screenshot. Twice rendered grey box due to Oculus Rift support.). Canvas 2 is still not on top of the first one and it's background is not transparent as well. As a reminder: The grey square is drawn via raw WebGL on a canvas called webglcanvas. The box is rendered via three.js on leapcanvas.
Code 1 (Initialization of canvases and divs [Xtend/Java]):
Browser::getDocument().getElementById("view").appendChild(webglDiv)
val Element webGLCanvasDiv = Browser::getDocument().createDivElement()
webGLCanvasDiv.style.setCssText("position: absolute")
webGLCanvasDiv.style.setCssText("z-index: 8")
webGLCanvasDiv.setId("webGLCanvasDiv")
Browser::getDocument().getElementById("webglDiv").appendChild(webGLCanvasDiv)
val webGLCanvas = Browser::getDocument().createCanvasElement()
webGLCanvas.setWidth(viewportWidth)
webGLCanvas.setHeight(viewportHeight)
webGLCanvas.style.setCssText("border-bottom: solid 1px #DDDDDD")
webGLCanvas.setId("webglcanvas")
Browser::getDocument().getElementById("webGLCanvasDiv").appendChild(webGLCanvas)
val Element webGLLeapDiv = Browser::getDocument().createDivElement()
webGLLeapDiv.style.setCssText("position: absolute")
webGLLeapDiv.style.setCssText("z-index: 10")
webGLLeapDiv.setId("webGLLeapDiv")
Browser::getDocument().getElementById("webglDiv").appendChild(webGLLeapDiv)
val leapCanvas = Browser::getDocument().createCanvasElement()
// canvas size is handled via renderer in Javascript
leapCanvas.setId("leapcanvas")
Browser::getDocument().getElementById("webGLLeapDiv").appendChild(leapCanvas)
Code 2 (Rendering of three.js scene [Javascript])
var foreground = $doc.getElementById("leapcanvas");
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
function init() {
camera = new $wnd.THREE.PerspectiveCamera(75, 500 / 500, 1, 10000);
camera.position.z = 500;
scene = new $wnd.THREE.Scene();
geometry = new $wnd.THREE.BoxGeometry(200, 200, 200);
material = new $wnd.THREE.MeshNormalMaterial();
mesh = new $wnd.THREE.Mesh(geometry, material);
scene.add(mesh);
renderer = new $wnd.THREE.WebGLRenderer({
canvas : foreground,
alpha : true
});
renderer.setSize(viewportWidth / 2, viewportHeight);
renderer.setClearColor(0x000000, 0);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
Assuming you have in your .html
<div id="container">
<div id="webglContainer"></div>
<div id="threeContainer"></div>
</div>
in your .css you need
/* make the canvases, children of the this container */
#container {
position: relative;
}
#webglContainer {
position: absolute;
pointer-events: none; /* this element will not catch any events */
z-index: 8; /* position this canvas at bottom of the other one */
}
#threeContainer {
position: absolute;
z-index: 10; /* position this canvas on top of the other one */
}
It's there any way to animate the "ghost image" when dragging the element?
Just like Google Drive, drag the files will trigger cool effect. I tried to add the CSS animation to the ghost-img, like this but it's not work.
go this way https://jsfiddle.net/mdvrkaer/4/ :
You miss the "drag" event. to move the ghost, make him follow the mouse cursor...
$(document).ready(function(){
dragAndDrop();
$('#ghost-img').hide();
});
function dragAndDrop(){
var dragElement = document.getElementById('row-1');
dragElement.addEventListener("dragstart",function(evt) {
$('#ghost-img').show();
//ghost image
var crt = document.getElementById('ghost-img').cloneNode(true);
crt.style.position = "absolute";
crt.style.top = "0px";
crt.style.right = "0px";
crt.style.zIndex = -1;
document.body.appendChild(crt);
evt.dataTransfer.setDragImage (crt, 0, 0);
},false);
dragElement.addEventListener("drag",function(evt) {
$('.ghost').css('top', evt.pageY);
$('.ghost').css('left', evt.pagex);
},false);
dragElement.addEventListener("dragend",function(evt) {
$('.ghost').hide();
},false);
}