<div class="label"></div>
<div class="page main">
  <div class="container">
    <div class="moveable"><span>Moveable</span></div>
    <div class="buttons able">
      <a data-able="scalable" class="selected">Scalable</a>
      <a data-able="resizable">Resizable</a>
      <a data-able="warpable">Warpable</a>
    </div>
    <p align="middle">Moveable is Draggable, Resizable, Scalable, Rotatable, Warpable, Pinchable</p>
    <p align="middle">
      <a href="https://github.com/daybrush/moveable" target="_blank"><strong>About Moveable</strong></a> /
      <a href="https://daybrush.com/moveable/release/latest/doc/" target="_blank"><strong>API</strong></a> /
      <a href="https://github.com/daybrush/scenejs-timeline" target="_blank"><strong>Main Project</strong></a>
    </p>
  </div>
</div>

@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,400,600&display=swap");
html, body {
  font-family: "Open Sans", sans-serif;
  position: relative;
  margin: 0;
  padding: 0;
  height: 100%;
  color: #333;
  letter-spacing: 1px;
  background: #f5f5f5;
  font-weight: 300;
}

a {
  text-decoration: none;
  color: #333;
}

.page {
  position: relative;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}

.page:nth-child(2n) {
  background: #f0f0f0;
}

.container {
  position: relative;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.moveable {
  font-family: "Roboto", sans-serif;
  position: relative;
  width: 250px;
  height: 200px;
  text-align: center;
  font-size: 40px;
  margin: 0px auto;
  font-weight: 100;
  letter-spacing: 1px;
}

.moveable span {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
}

.description {
  text-align: center;
}

.badges {
  padding: 10px;
  text-align: center;
}

.badges img {
  height: 20px;
}

.buttons.able a {
  min-width: auto;
  padding: 8px 20px;
}

.buttons {
  text-align: center;
  margin: 0;
  padding: 10px;
}

.buttons a {
  margin: 0;
  position: relative;
  text-decoration: none;
  color: #333;
  border: 1px solid #333;
  padding: 12px 30px;
  min-width: 140px;
  text-align: center;
  /* font-weight: 400; */
  display: inline-block;
  box-sizing: border-box;
  margin: 5px;
  transition: all ease 0.5s;
}

.buttons a:hover, .buttons a.selected {
  background: #333;
  color: #fff;
}

.page.main {
  z-index: 1;
  min-height: 700px;
}

.label {
  position: fixed;
  top: 0;
  left: 0;
  padding: 5px;
  border-radius: 5px;
  background: #333;
  z-index: 10;
  color: #fff;
  font-weight: bold;
  font-size: 12px;
  display: none;
  transform: translate(-100%, -100%);
}

.page.feature, .page.footer {
  height: auto;
  text-align: left;
  padding: 60px 20px;
}

.page.feature .container, .page.footer .container {
  top: 0;
  left: 0;
  padding: 60px 0px;
  margin: auto;
  transform: none;
  width: auto;
  max-width: 800px;
}

.page.feature .container {
  display: flex;
}

.page.footer .container {
  padding: 0px;
}

.page.feature h2.container {
  padding: 10px 0px;
  font-weight: 300;
  font-size: 40px;
}

.feature .container .left {
  position: relative;
  width: 300px;
  height: 205px;
  display: inline-block;
  vertical-align: top;
  z-index: 2000;
  margin-bottom: 20px;
}

.feature .container .right {
  position: relative;
  display: inline-block;
  vertical-align: top;
  flex: 1;
}

.feature .right .description {
  text-align: left;
  margin: 0px 0px 10px;
}

.feature .right .description strong {
  font-weight: 600;
}

.draggable, .resizable, .scalable, .rotatable, .origin, .warpable, .pinchable {
  position: absolute;
  left: 0;
}

.origin {
  transform-origin: 30% 50%;
}

pre {
  position: relative;
  border: 1px solid #ccc;
  box-sizing: border-box;
  padding: 10px;
  max-width: 500px;
}

code.hljs {
  padding: 0;
}

.tab {
  padding: 10px 12px;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 1px solid #ccc;
  box-shadow: none;
  font-weight: bold;
  margin: 0;
  cursor: pointer;
  outline: none;
}

.tab.selected {
  background: #333;
  color: #fff;
  border: 1px solid #333;
}

.panel {
  display: none;
}

.panel.selected {
  display: block;
}

.page.footer {
  font-weight: 400;
}

.page.footer a {
  text-decoration: underline;
}

.page.footer span:first-child:before {
  content: "";
}

.page.footer span:before {
  content: "/";
}

const frame = new Scene.Frame({
  width: "250px",
  height: "200px",
  left: "0px",
  top: "0px",
  transform: {
    rotate: "0deg",
    scaleX: 1,
    scaleY: 1,
    matrix3d: [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ],
  },
});

const moveableElement = document.querySelector(".moveable");
const moveable = new Moveable(moveableElement.parentElement, {
  target: moveableElement,
  origin: false,
  draggable: true,
  rotatable: true,
  scalable: true,
  pinchable: true,
  keepRatio: false,
  throttleDrag: 1,
  throttleScale: 0.01,
  throttleRotate: 0.2,
  throttleResize: 1,
}).on("pinch", ({ clientX, clientY }) => {
  setTimeout(() => {
    setLabel(clientX, clientY, `X: ${frame.get("left")}
<br/>Y: ${frame.get("top")}
<br/>W: ${frame.get("width")}
<br/>H: ${frame.get("height")}
<br/>S: ${frame.get("transform", "scaleX").toFixed(2)}, ${frame.get("transform", "scaleY").toFixed(2)}
<br/>R: ${parseFloat(frame.get("transform", "rotate")).toFixed(1)}deg
`);
  });
}).on("drag", ({ target, left, top, clientX, clientY, isPinch }) => {
  frame.set("left", `${left}px`);
  frame.set("top", `${top}px`);
  setTransform(target);
  !isPinch && setLabel(clientX, clientY, `X: ${left}px<br/>Y: ${top}px`);

}).on("scale", ({ target, delta, clientX, clientY, isPinch }) => {
  const scaleX = frame.get("transform", "scaleX") * delta[0];
  const scaleY = frame.get("transform", "scaleY") * delta[1];
  frame.set("transform", "scaleX", scaleX);
  frame.set("transform", "scaleY", scaleY);
  setTransform(target);
  !isPinch && setLabel(clientX, clientY, `S: ${scaleX.toFixed(2)}, ${scaleY.toFixed(2)}`);

}).on("rotate", ({ target, beforeDelta, clientX, clientY, isPinch }) => {
  const deg = parseFloat(frame.get("transform", "rotate")) + beforeDelta;

  frame.set("transform", "rotate", `${deg}deg`);
  setTransform(target);
  !isPinch && setLabel(clientX, clientY, `R: ${deg.toFixed(1)}`);
}).on("resize", ({ target, width, height, clientX, clientY, isPinch }) => {
  frame.set("width", `${width}px`);
  frame.set("height", `${height}px`);
  setTransform(target);
  !isPinch &&  setLabel(clientX, clientY, `W: ${width}px<br/>H: ${height}px`);
}).on("warp", ({ target, multiply, delta, clientX, clientY }) => {
  frame.set("transform", "matrix3d", multiply(frame.get("transform", "matrix3d"), delta));
  setTransform(target);
  setLabel(clientX, clientY, `X: ${clientX}px<br/>Y: ${clientY}px`);
}).on("dragEnd", () => {
  labelElement.style.display = "none";
}).on("scaleEnd", () => {
  labelElement.style.display = "none";
}).on("rotateEnd", () => {
  labelElement.style.display = "none";
}).on("resizeEnd", () => {
  labelElement.style.display = "none";
}).on("warpEnd", () => {
  labelElement.style.display = "none";
});


window.addEventListener("resize", () => {
    moveable.updateRect();
});
const labelElement = document.querySelector(".label");

function setTransform(target) {
  target.style.cssText = frame.toCSS();
}
function setLabel(clientX, clientY, text) {
  labelElement.style.cssText = `
display: block; transform: translate(${clientX}px, ${clientY - 10}px) translate(-100%, -100%);`;
  labelElement.innerHTML = text;
}

document.querySelector(`.able [data-able="scalable"]`).addEventListener("click", e => {
  document.querySelector(`.able .selected`).classList.remove("selected");
   e.target.classList.add("selected");
  moveable.scalable = true;
  moveable.resizable = false;
  moveable.warpable = false;
});
document.querySelector(`.able [data-able="resizable"]`).addEventListener("click", e => {
  document.querySelector(`.able .selected`).classList.remove("selected");
   e.target.classList.add("selected");
  moveable.scalable = false;
  moveable.resizable = true;
  moveable.warpable = false;
});
document.querySelector(`.able [data-able="warpable"]`).addEventListener("click", e => {
  document.querySelector(`.able .selected`).classList.remove("selected");
   e.target.classList.add("selected");
  moveable.scalable = false;
  moveable.resizable = false;
  moveable.warpable = true;
});
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://daybrush.com/scenejs/release/latest/dist/scene.js
  2. https://daybrush.com/moveable/release/latest/dist/moveable.min.js