Is it possible in Konva to animate a shape (marker, circle) along a lline/path. I tried to manually calculate positions over time but this is only feasible if the line is straight from A to B, but I'm interested in a bezier curve and multiple path points.
So I wonder if Konva supports this kind of thing or someone could give a direction how to approach this.
As you have identified, the Path object has some handy methods in getLength() to find the overall path length and getPointAtLength() which can then be used to find the (x,y) at any given point along the length.
In case it helps anyone, I built the path data from the output of another snippet from this other question.
var data = [{"x":34,"y":34},{"x":84,"y":64},{"x":141,"y":79},{"x":181.5,"y":78.5},{"x":218,"y":62},{"x":223,"y":40},{"x":240,"y":26},{"x":259.5,"y":25},{"x":271,"y":40},{"x":292.5,"y":53},{"x":311.25,"y":55.5},{"x":330.625,"y":46.75},{"x":332.3125,"y":30.375},{"x":349.15625,"y":10.1875},{"x":374.578125,"y":10.09375},{"x":392,"y":26},{"x":411,"y":36},{"x":444.5,"y":37},{"x":453.875,"y":27.25},{"x":463.25,"y":17.5},{"x":472.9375,"y":10.625},{"x":494.625,"y":15.75},{"x":530,"y":48},{"x":534,"y":88},{"x":540,"y":150},{"x":552,"y":198},{"x":544,"y":227},{"x":522,"y":256},{"x":504.5,"y":263},{"x":471,"y":262},{"x":448,"y":252},{"x":372,"y":214},{"x":290,"y":146},{"x":256,"y":100},{"x":198,"y":104},{"x":182,"y":140},{"x":204,"y":185},{"x":203,"y":201.5},{"x":190,"y":214},{"x":174.5,"y":218},{"x":155,"y":214},{"x":124,"y":222},{"x":113.5,"y":232.5},{"x":95,"y":227},{"x":75.5,"y":211.5},{"x":72,"y":188},{"x":58,"y":136}]
// Set up the canvas / stage
var stage = new Konva.Stage({container: 'container1', width: 600, height: 300});
// Add a layer for line
var layer = new Konva.Layer({draggable: false});
stage.add(layer);
// draw a path.
var path = new Konva.Path({
x: 0,
y: 0,
stroke: 'cyan'
});
layer.add(path)
// Load the path points up using M = moveto, L = lineto.
var p = "M" + data[0].x + " " + data[0].y;
for (var i = 1; i < data.length; i = i + 1){
p = p + " L" + data[i].x + " " + data[i].y;
}
path.setData(p);
// add a circle to be animated along the path
var circle = new Konva.Circle({ x: data[0].x, y: data[0].y, radius: 10, fill: 'Magenta'});
layer.add(circle);
stage.draw();
$('#reset').on('click', function(){
// Now animate a circle along the path
var steps = 50; // number of steps in animation
var pathLen = path.getLength();
var step = pathLen / steps;
var frameCnt = 0, pos =0, pt;
anim = new Konva.Animation(function(frame) {
pos = pos + 1;
pt = path.getPointAtLength(pos * step);
circle.position({x: pt.x, y: pt.y});
}, layer);
anim.start();
})
$('#reset').trigger('click');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/2.5.1/konva.min.js"></script>
<button style='position: absolute; z-index: 10;' id='reset'>Go</button>
<div id='container1' style="width: 300px, height: 200px; background-color: silver;"></div>
<div id='img'></div>
I have a rectangle. I want to split it into a number of nonoverlapping smaller rectangles. Any good data structure to represent the partition?
Ok this could be interesting, how about:
var w = $('#rect').width();
var h = $('#rect').height();
var ratio = h/w;
var splitAcross = 10;//Ten divisions squared *Changeable*
var splitW = w/splitAcross;
var splitH = ratio*splitW;//Split across and down
var divRects = document.createElement("div"); // Create with DOM
divRects.style.width = splitW+'px';
divRects.style.height = splitH+'px';
var span = 0;
var spl = 0;
while (span < splitAcross)
{
spl++;
var cln = divRects.cloneNode(true);
if (splitW*spl % w == 0) span++;
$("#rect").append(cln);
}
//Removeable*** this is just to show divisions
$("#rect div").each(function (){
$(this).animate({
left: ($(this).offset().left-(w/2))*0.1,
top: ($(this).offset().top-(h/2))*0.1
}, 1000).animate({
left: 0,
top: 0
}, 1000);
});
//Removeable
#rect div {
border: 1px solid #eee;
margin: -1px;
float: left;
position: relative;
background:#5fba7d;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="rect" style="width:450px; height:200px"></div>
<!--width:450px; height:200px *Changeable*-->
I would like to draw a line binding a entity to its label with an offset.
CesiumJS allows to offset the label, however its not possible to draw a line (or polyline) from a position to an offset like the red line in this image.
How can i do it? any sugestion?
i'm using pixel offset. but there is no problem to use eye offset
labels.add({
position: Cesium.Cartesian3.fromDegrees(-75.1641667, 29.9522222),
text: 'Another label',
pixelOffset: new Cesium.Cartesian2(100,-100)
});
The best way to do this is probably a billboard with an image of the line on it. The length will never change if it's a pixelOffset. You can put an image of a white line, and use the color property to set any other color.
var viewer = new Cesium.Viewer('cesiumContainer', {
navigationInstructionsInitiallyVisible: false, animation: false, timeline: false,
// These next 6 lines are just to avoid Stack Snippet error messages.
imageryProvider : new Cesium.TileMapServiceImageryProvider({
url: Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"),
}),
baseLayerPicker : false,
geocoder : false,
infoBox : false
});
var scene = viewer.scene;
var offsetX = 50, offsetY = -80;
var pos = Cesium.Cartesian3.fromDegrees(-75.1641667, 29.9522222);
var labels = scene.primitives.add(new Cesium.LabelCollection());
labels.add({
position: pos,
text: 'Another label',
pixelOffset: new Cesium.Cartesian2(offsetX, offsetY)
});
var canvas = document.createElement('canvas');
canvas.width = Math.abs(offsetX);
canvas.height = Math.abs(offsetY);
var context2D = canvas.getContext('2d');
context2D.beginPath();
context2D.lineWidth = 3;
context2D.strokeStyle = '#ffffff';
context2D.moveTo((offsetX < 0) ? -offsetX : 0, (offsetY < 0) ? -offsetY : 0);
context2D.lineTo((offsetX < 0) ? 0 : offsetX, (offsetY < 0) ? 0 : offsetY);
context2D.stroke();
var billboards = scene.primitives.add(new Cesium.BillboardCollection());
var billboard = billboards.add({
color : Cesium.Color.RED,
image : canvas,
pixelOffset: new Cesium.Cartesian2(offsetX * 0.5, offsetY * 0.5),
position : pos
});
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
}
<link href="https://cesium.com/downloads/cesiumjs/releases/1.78/Build/Cesium/Widgets/widgets.css" rel="stylesheet"/>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.78/Build/Cesium/Cesium.js"></script>
<div id="cesiumContainer"></div>
I have it so that you can draw under the image, but you must start drawing outside of the image border and move under the image. So you can't start right on the image. How do I fix this?
My html is just:
<canvas id="canvas"></canvas>
<img id="html5" src="http://i.imgur.com/Wpx3Vum.png" style="position:absolute;left:100px;top:15px;" />
You can use two canvas elements instead:
Put two canvas elements on top of each other
Draw the image you want to superimpose on the top canvas
Add mouse event listener to the top canvas
When drawing read the positions from top canvas but draw the lines to the bottom one using the bottom canvas' context
(links just meant as examples)
An example of this could be including a simple basic draw function:
Online demo
HTML:
<div id="cont">
<canvas id="canvasBottom" width=400 height=400></canvas>
<canvas id="canvasTop" width=400 height=400></canvas>
</div>
CSS:
#cont {
position:relative;
border:1px solid #777;
height:400px;
}
#cont > canvas {
position:absolute;
left:0;
top:0;
cursor:crosshair;
}
JavaScript:
Load and set the image to top layer:
var img = new Image,
canvas = document.getElementById('canvasBottom'),
canvasTop = document.getElementById('canvasTop'),
ctx = canvas.getContext('2d'),
ctxMouse = canvasTop.getContext('2d');
img.onload = setup;
img.src = 'http://i.imgur.com/HNiER0v.png';
ctx.lineWidth = 5;
ctx.strokeStyle = 'rgb(0, 100, 255)';
function setup() {
ctxMouse.drawImage(this, 0, 0);
}
Handle mouse:
var px, py, isDown = false;
canvasTop.onmousedown = function (e) {
var pos = getXY(e);
px = pos.x;
py = pos.y;
isDown = true;
}
canvasTop.onmouseup = function () {
isDown = false;
}
canvasTop.onmousemove = function (e) {
if (isDown) {
var pos = getXY(e);
ctx.beginPath();
ctx.moveTo(px, py);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
px = pos.x;
py = pos.y;
}
}
function getXY(e) {
var rect = canvasTop.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}
As you can see you can now draw behind the initial drawing:
(Now, if that is not a master piece than I don't know what a master piece is..!)
I have a page whose background image scales as the browser re-sizes. What I'm trying to figure out is how to make the blue marker image scale and maintain position in proportion to the background. For example, if the blue marker were on the tip of the harbor, as I scaled the browser down, I'd want that marker to stay on the tip of the harbor and shrink in size proportionally with the browser's new dimensions.
Anyone have any ideas that will point me in the right direction?
I used the below method to find the browser size, but I think my math's a little off. You can view the live example here:
http://mikeheavers.com/stage/full_screen_map/
The marker gets really off if you scale it in one direction more than the other.
<style type="text/css">
html {
background: url(images/antigua.jpeg) no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
#infoDiv {
background: white;
padding: 20px;
width: 200px;
position:absolute;
}
#map_canvas {
position:absolute ;
}
.marker {
position:absolute;
left: 800px;
top: 400px;
height: 20px;
width: 20px;
background: #ff0000;
}
</style>
<script type="text/javascript" charset="utf-8" src="js/jquery-1.4.4.min.js"></script>
<div id="infoDiv">
</div>
<div id="markers">
<div class="marker">
</div>
</div>
<script type="text/javascript">
$(document).ready(function()
{
var myWidth = window.innerWidth;
var leftVal = $("#markers").children().css('left');
var leftValNumber = parseFloat(leftVal);
var leftRatio = leftValNumber / myWidth;
var leftValPos;
var myHeight = window.innerHeight;
var topVal = $("#markers").children().css('top');
var topValNumber = parseFloat(topVal);
var topRatio = topValNumber / myHeight;
var topValPos;
var scaleRatio;
if (myWidth > myHeight){
scaleRatio = 20/myWidth;
} else {
scaleRatio = 20/myHeight;
}
window.onresize = function() {
sizeMarkers();
}
function init()
{
sizeMarkers();
}
function sizeMarkers()
{
if( typeof( window.innerWidth ) == 'number' ) {
//Non-IE
myWidth = window.innerWidth;
myHeight = window.innerHeight;
} else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) {
//IE 4 compatible
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
topVal = $("#markers").children().css('top');
topValNumber = parseFloat(topVal);
topValPos = topRatio * myHeight;
leftVal = $("#markers").children().css('top');
leftValNumber = parseFloat(leftVal);
leftValPos = leftRatio * myWidth;
if (myWidth < myHeight){
$("#markers").children().width(myWidth*scaleRatio);
$("#markers").children().height(myWidth*scaleRatio);
} else {
$("#markers").children().width(myHeight*scaleRatio);
$("#markers").children().height(myHeight*scaleRatio);
}
$("#markers").children().css('top',topValPos);
$("#markers").children().css('left',leftValPos);
$("#infoDiv").html( 'Width = ' + myWidth + ' | Height = ' + myHeight + ' | Top Value: ' + topValPos + ' | Left Value: ' + leftValPos);
}
init();
});
</script>
You could adjust width of your marker using JavaScript by keeping at any onresize events the same ratio marker-width and page-width.
To know your viewport width :
http://www.howtocreate.co.uk/tutorials/javascript/browserwindow
Hope it helps.