Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <script src="https://unpkg.com/konva@8/konva.min.js"></script>
<div id="container1" class='container'></div>
<div class='buttons'>
<button id='stage1ZoomIn'>Zoom in Stage #1</button>
<button id='stage1ZoomOut'>Zoom out Stage #1</button>
</div>
<div id="container2" class='container'></div>

<div class='buttons'>
<button id='stage2ZoomIn'>Zoom in Stage #2</button>
<button id='stage2ZoomOut'>Zoom out Stage #2</button>
</div>
<div class='buttons'>
<button id='reset'>Reset stages</button>
</div>



              
            
!

CSS

              
                body {
  margin: 10;
  padding: 10;
  overflow: hidden;
  background-color: #f0f0f0;
}
.info { min-height: 18px; margin: 5px 20px; }
.container {
  border: 1px solid black;
  margin:  20px 0 0 20px; 
  max-width: 400px;
}
.buttons {
  margin:  20px 0 0 20px; 
  max-width: 400px;
}

              
            
!

JS

              
                // All groups have their position as (0,0) unless changed.

// Set up a stage
let stage1 = new Konva.Stage({
    container: "container1",
    width: 400,
    height: 250
  }),
  // add a layer to draw on
  layer1 = new Konva.Layer(),
  stage2 = new Konva.Stage({
    container: "container2",
    width: 400,
    height: 250
  }),
  // add a layer to draw on
  layer2 = new Konva.Layer(),
  stageRect = new Konva.Rect({
    size: stage1.size(),
    stroke: "lime"
  }),
  star = new Konva.Star({
    innerRadius: 10,
    outerRadius: 25,
    fill: "red",
    stroke: "black",
    strokeWidth: 5,
    numPoints: 5,
    x: 60,
    y: 60,
    draggable: true,
    shadowOffset: { x: 5, y: 5 },
    shadowColor: "black",
    shadowBlur: 5,
    shadowOpacity: 0.5,
    shadowForStrokeEnabled: false
  }),
  clone,
  circle1 = new Konva.Circle({
    x: 200,
    y: 150,
    radius: 10,
    fill: "magenta"
  }),
  circle2 = circle1.clone(),
  zoomStep = 0.1;

layer1.add(stageRect);
layer2.add(stageRect.clone());

// Stick 40 stars into each stage
for (var n = 0; n < 40; n++) {
  let clone1 = star.clone({
    x: Math.random() * stage1.width() * (1 / stage1.scaleX()),
    y: Math.random() * stage1.height() * (1 / stage1.scaleY())
  });
  layer1.add(clone1);
  let clone2 = clone1.clone();
  layer2.add(clone2);
  clone1.cache();
  clone2.cache();
}

// add the circle to each stage - this is the zoom point for buttons
layer1.add(circle1);
layer2.add(circle2);

// Add the layer to the stage and shapes to layer
stage1.add(layer1);
stage2.add(layer2);

let scale1 = 1;
$("#stage1ZoomIn").on("click", function () {
  zoomStage1(zoomStep);
});
$("#stage1ZoomOut").on("click", function () {
  zoomStage1(-zoomStep);
});

function zoomStage1(inc) {
  scale1 = scale1 + inc;
  stage1.setAttrs({ scaleX: scale1, scaleY: scale1 });
}

let scale2 = 1;
$("#stage2ZoomIn").on("click", function () {
  scale2 = zoomStage2(stage2, circle2.position(), scale2, zoomStep);
});
$("#stage2ZoomOut").on("click", function () {
  scale2 = zoomStage2(stage2, circle2.position(), scale2, -zoomStep);
});

stage1.on("wheel", function (e) {
  e.evt.preventDefault();

  var pointer = stage2.getPointerPosition();

  zoomInc = e.evt.deltaY > 0 ? -zoomStep : zoomStep;

  scale2 = zoomStage2(stage2, pointer, scale2, zoomInc);
});

stage2.on("wheel", function (e) {
  e.evt.preventDefault();

  var pointer = stage2.getPointerPosition();

  zoomInc = e.evt.deltaY > 0 ? -zoomStep : zoomStep;

  scale2 = zoomStage2(stage2, pointer, scale2, zoomInc);
});

$("#reset").on("click", function () {
  scale1 = 1;
  stage1.scale({ x: scale1, y: scale1 });

  scale2 = 1;
  stage2.scale({ x: scale1, y: scale1 });
  stage2.position({ x: 0, y: 0 });
});

/* Zoom the stage at the given position
  Parameters:
   stage: the stage to be zoomed.
   zoomPoint: the (x, y) for centre of zoom.
   zoomBefore: the zoom factor at the start of the process.
   inc : the amount of zoom to apply.
   returns: zoom factor after zoom completed.
*/
function zoomStage2(stage, zoomPoint, zoomBefore, inc) {
  // remember the scale before new zoom is applied - we are scaling 
  // same in x & y so either will work
  let oldScale = stage.scaleX();

  // compute the distance to the zoom point before applying zoom
  var mousePointTo = {
    x: (zoomPoint.x - stage.x()) / oldScale,
    y: (zoomPoint.y - stage.y()) / oldScale
  };

  // compute new scale
  let zoomAfter = zoomBefore + inc;

  // apply new zoom to stage
  stage.scale({ x: zoomAfter, y: zoomAfter });

  // Important - move the stage so that the zoomed point remains 
  // visually in place
  var newPos = {
    x: zoomPoint.x - mousePointTo.x * zoomAfter,
    y: zoomPoint.y - mousePointTo.y * zoomAfter
  };
  
  // Apply position to stage
  stage.position(newPos);

  // return the new zoom factor.
  return zoomAfter;
}

              
            
!
999px

Console