Skip to main content

Make Animation App

 You can make animation tools using chatgpt. or Simple you can copy this html code to make your own animation app.


<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <title>Editable Drawing App with Frames</title>

  <style>

    body {

      font-family: Arial, sans-serif;

      margin: 20px;

      display: flex;

      flex-direction: column;

      align-items: center;

    }

    #toolbar, #framebar {

      margin-bottom: 10px;

    }

    button, select, label {

      margin-right: 5px;

      padding: 5px 10px;

    }

    #canvasContainer {

      position: relative;

    }

    canvas {

      border: 1px solid #ccc;

      cursor: crosshair;

    }

  </style>

</head>

<body>

  <h2>Editable Drawing App with Frames</h2>

  

  <!-- Toolbar for drawing tools and deletion -->

  <div id="toolbar">

    <button id="freeDrawBtn">Free Draw</button>

    <button id="lineBtn">Line</button>

    <button id="rectBtn">Rectangle</button>

    <button id="circleBtn">Circle</button>

    <button id="selectBtn">Select/Move</button>

    <button id="deleteBtn">Delete Object</button>

    <span style="margin-left: 20px;">Current Tool: <span id="currentTool">Free Draw</span></span>

    <label>

      <input type="checkbox" id="gridCheckbox"> Grid

    </label>

  </div>

  

  <!-- Frame controls -->

  <div id="framebar">

    <button id="addFrameBtn">Add Frame</button>

    <select id="frameSelect"></select>

    <button id="playbackBtn">Playback</button>

    <label>

      <input type="checkbox" id="loopCheckbox"> Frame Loop

    </label>

  </div>

  

  <!-- Canvas container -->

  <div id="canvasContainer">

    <canvas id="drawingCanvas" width="800" height="500"></canvas>

  </div>

  

  <script>

  /* ========= Data Structures ========= */

  // Each shape is stored as an object with a type and properties.

  class Shape {

    constructor(type, props) {

      this.type = type; // "free", "line", "rect", "circle"

      this.props = props; // e.g., free: {points: [...]}, others: {start, end}

      this.selected = false;

    }

    

    // Draw shape and, if selected, its resize handles

    draw(ctx) {

      ctx.save();

      ctx.strokeStyle = this.selected ? "red" : "black";

      ctx.lineWidth = 2;

      switch(this.type) {

        case "free":

          ctx.beginPath();

          this.props.points.forEach((pt, i) => {

            if (i === 0) ctx.moveTo(pt.x, pt.y);

            else ctx.lineTo(pt.x, pt.y);

          });

          ctx.stroke();

          break;

        case "line":

          ctx.beginPath();

          ctx.moveTo(this.props.start.x, this.props.start.y);

          ctx.lineTo(this.props.end.x, this.props.end.y);

          ctx.stroke();

          break;

        case "rect":

          var x = Math.min(this.props.start.x, this.props.end.x);

          var y = Math.min(this.props.start.y, this.props.end.y);

          var w = Math.abs(this.props.end.x - this.props.start.x);

          var h = Math.abs(this.props.end.y - this.props.start.y);

          ctx.strokeRect(x, y, w, h);

          break;

        case "circle":

          var dx = this.props.end.x - this.props.start.x;

          var dy = this.props.end.y - this.props.start.y;

          var radius = Math.sqrt(dx*dx + dy*dy);

          ctx.beginPath();

          ctx.arc(this.props.start.x, this.props.start.y, radius, 0, 2*Math.PI);

          ctx.stroke();

          break;

      }

      if (this.selected) {

        // Draw resize handles at corners of bounding box

        const bbox = this.boundingBox();

        const handles = getHandles(bbox);

        ctx.fillStyle = "blue";

        handles.forEach(h => {

          ctx.fillRect(h.x - 4, h.y - 4, 8, 8);

        });

      }

      ctx.restore();

    }

    

    // Returns the bounding box {x, y, w, h} of the shape

    boundingBox() {

      switch(this.type) {

        case "free":

          const xs = this.props.points.map(pt => pt.x);

          const ys = this.props.points.map(pt => pt.y);

          const x = Math.min(...xs);

          const y = Math.min(...ys);

          return { x, y, w: Math.max(...xs) - x, h: Math.max(...ys) - y };

        case "line":

          const x1 = Math.min(this.props.start.x, this.props.end.x);

          const y1 = Math.min(this.props.start.y, this.props.end.y);

          return { x: x1, y: y1, w: Math.abs(this.props.end.x - this.props.start.x), h: Math.abs(this.props.end.y - this.props.start.y) };

        case "rect":

          const rx = Math.min(this.props.start.x, this.props.end.x);

          const ry = Math.min(this.props.start.y, this.props.end.y);

          return { x: rx, y: ry, w: Math.abs(this.props.end.x - this.props.start.x), h: Math.abs(this.props.end.y - this.props.start.y) };

        case "circle":

          const dx = this.props.end.x - this.props.start.x;

          const dy = this.props.end.y - this.props.start.y;

          const r = Math.sqrt(dx*dx + dy*dy);

          return { x: this.props.start.x - r, y: this.props.start.y - r, w: 2*r, h: 2*r };

      }

    }

    

    // Check if a point (x,y) is on or near this shape (for selection purposes)

    hitTest(x, y) {

      const buffer = 5;  // Tolerance

      switch(this.type) {

        case "free":

          for (let pt of this.props.points) {

            if (Math.abs(pt.x - x) < buffer && Math.abs(pt.y - y) < buffer) return true;

          }

          break;

        case "line":

          const { start, end } = this.props;

          const A = x - start.x, B = y - start.y;

          const C = end.x - start.x, D = end.y - start.y;

          const dot = A * C + B * D;

          const len_sq = C * C + D * D;

          let param = len_sq ? dot / len_sq : -1;

          let xx, yy;

          if (param < 0) { xx = start.x; yy = start.y; }

          else if (param > 1) { xx = end.x; yy = end.y; }

          else { xx = start.x + param * C; yy = start.y + param * D; }

          if (Math.sqrt((x - xx)**2 + (y - yy)**2) < buffer) return true;

          break;

        case "rect":

          const bbox = this.boundingBox();

          if (x >= bbox.x && x <= bbox.x + bbox.w && y >= bbox.y && y <= bbox.y + bbox.h) return true;

          break;

        case "circle":

          const dxC = x - this.props.start.x;

          const dyC = y - this.props.start.y;

          const r = Math.sqrt((this.props.end.x - this.props.start.x)**2 + (this.props.end.y - this.props.start.y)**2);

          if (Math.abs(Math.sqrt(dxC*dxC + dyC*dyC) - r) < buffer) return true;

          break;

      }

      return false;

    }

    

    // Move the shape by (dx,dy)

    move(dx, dy) {

      if(this.type === "free") {

        this.props.points.forEach(pt => { pt.x += dx; pt.y += dy; });

      } else if(this.type === "line" || this.type === "rect" || this.type === "circle") {

        this.props.start.x += dx;

        this.props.start.y += dy;

        this.props.end.x += dx;

        this.props.end.y += dy;

      }

    }

  }

  

  // Each frame holds an array of shapes

  class Frame {

    constructor() {

      this.shapes = [];

    }

    draw(ctx) {

      this.shapes.forEach(shape => shape.draw(ctx));

    }

  }

  

  /* ========= Global Variables ========= */

  let currentTool = "free"; // Options: free, line, rect, circle, select

  let drawing = false;

  let currentShape = null;

  let selectedShape = null;

  

  // Variables for moving/resizing

  let resizing = false;

  let currentResizeHandle = null;

  let resizeStartX = 0, resizeStartY = 0;

  let originalProps = null; // A deep copy of props for the shape before resizing

  

  const frames = [];

  let currentFrameIndex = 0;

  

  const canvas = document.getElementById("drawingCanvas");

  const ctx = canvas.getContext("2d");

  

  // Utility: Returns the four corner handle positions from a bounding box.

  function getHandles(bbox) {

    return [

      { name: "tl", x: bbox.x, y: bbox.y },

      { name: "tr", x: bbox.x + bbox.w, y: bbox.y },

      { name: "bl", x: bbox.x, y: bbox.y + bbox.h },

      { name: "br", x: bbox.x + bbox.w, y: bbox.y + bbox.h }

    ];

  }

  

  // Check if (x,y) is over a handle of the currently selected shape.

  function checkHandleHit(x, y, shape) {

    const bbox = shape.boundingBox();

    const handles = getHandles(bbox);

    for (let handle of handles) {

      if (Math.abs(handle.x - x) < 6 && Math.abs(handle.y - y) < 6) {

        return handle.name;

      }

    }

    return null;

  }

  

  function updateToolDisplay() {

    document.getElementById("currentTool").textContent =

      currentTool === "free" ? "Free Draw" :

      currentTool === "line" ? "Line" :

      currentTool === "rect" ? "Rectangle" :

      currentTool === "circle" ? "Circle" :

      "Select/Move";

  }

  

  /* ========= Frame Management ========= */

  function addFrame() {

    frames.push(new Frame());

    currentFrameIndex = frames.length - 1;

    updateFrameSelect();

    redraw();

  }

  

  function updateFrameSelect() {

    const frameSelect = document.getElementById("frameSelect");

    frameSelect.innerHTML = "";

    frames.forEach((frame, index) => {

      const option = document.createElement("option");

      option.value = index;

      option.text = "Frame " + (index + 1);

      if(index === currentFrameIndex) option.selected = true;

      frameSelect.appendChild(option);

    });

  }

  

  function selectFrame(index) {

    currentFrameIndex = index;

    redraw();

  }

  

  /* ========= Grid Drawing ========= */

  function drawGrid() {

    const gridOn = document.getElementById("gridCheckbox").checked;

    if (!gridOn) return;

    const step = 25;

    ctx.save();

    ctx.strokeStyle = "#eee";

    ctx.lineWidth = 1;

    for (let x = 0; x <= canvas.width; x += step) {

      ctx.beginPath();

      ctx.moveTo(x, 0);

      ctx.lineTo(x, canvas.height);

      ctx.stroke();

    }

    for (let y = 0; y <= canvas.height; y += step) {

      ctx.beginPath();

      ctx.moveTo(0, y);

      ctx.lineTo(canvas.width, y);

      ctx.stroke();

    }

    ctx.restore();

  }

  

  /* ========= Redraw Canvas ========= */

  function redraw() {

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    drawGrid();

    if(frames[currentFrameIndex]) frames[currentFrameIndex].draw(ctx);

  }

  

  /* ========= Mouse Event Handlers ========= */

  canvas.addEventListener("mousedown", (e) => {

    const rectCanvas = canvas.getBoundingClientRect();

    const x = e.clientX - rectCanvas.left;

    const y = e.clientY - rectCanvas.top;

    drawing = true;

    

    // If in Select/Move mode

    if(currentTool === "select") {

      // If an object is already selected, check if a resize handle is hit.

      if(selectedShape) {

        const handle = checkHandleHit(x, y, selectedShape);

        if(handle) {

          resizing = true;

          currentResizeHandle = handle;

          resizeStartX = x;

          resizeStartY = y;

          // Deep copy the original properties to use as a baseline for resizing.

          originalProps = JSON.parse(JSON.stringify(selectedShape.props));

          return;

        }

      }

      

      // Otherwise try to select an object.

      const shapes = frames[currentFrameIndex].shapes;

      selectedShape = null;

      // Check in reverse order to pick the topmost object.

      for(let i = shapes.length - 1; i >= 0; i--) {

        if(shapes[i].hitTest(x, y)) {

          selectedShape = shapes[i];

          selectedShape.selected = true;

          break;

        }

      }

      redraw();

      return;

    }

    

    // For drawing new objects.

    selectedShape = null;

    switch(currentTool) {

      case "free":

        currentShape = new Shape("free", { points: [{ x, y }] });

        break;

      case "line":

        currentShape = new Shape("line", { start: { x, y }, end: { x, y } });

        break;

      case "rect":

        currentShape = new Shape("rect", { start: { x, y }, end: { x, y } });

        break;

      case "circle":

        currentShape = new Shape("circle", { start: { x, y }, end: { x, y } });

        break;

    }

  });

  

  canvas.addEventListener("mousemove", (e) => {

    if(!drawing) return;

    const rectCanvas = canvas.getBoundingClientRect();

    const x = e.clientX - rectCanvas.left;

    const y = e.clientY - rectCanvas.top;

    

    // If drawing a new shape.

    if(currentShape && currentTool !== "select") {

      if(currentTool === "free") {

        currentShape.props.points.push({ x, y });

      } else {

        currentShape.props.end = { x, y };

      }

      redraw();

      currentShape.draw(ctx);

      return;

    }

    

    // If in select mode and resizing.

    if(currentTool === "select" && resizing && selectedShape) {

      // Determine new parameters based on the handle dragged.

      let dx = x - resizeStartX;

      let dy = y - resizeStartY;

      // Get original bounding box and recompute new one according to handle.

      let origBox;

      if(selectedShape.type === "free") {

        const xs = originalProps.points.map(pt => pt.x);

        const ys = originalProps.points.map(pt => pt.y);

        origBox = { x: Math.min(...xs), y: Math.min(...ys), w: Math.max(...xs) - Math.min(...xs), h: Math.max(...ys) - Math.min(...ys) };

      } else {

        origBox = {

          x: Math.min(originalProps.start.x, originalProps.end.x),

          y: Math.min(originalProps.start.y, originalProps.end.y),

          w: Math.abs(originalProps.end.x - originalProps.start.x),

          h: Math.abs(originalProps.end.y - originalProps.start.y)

        };

      }

      let newBox = Object.assign({}, origBox);

      if(currentResizeHandle === "tl") {

        newBox.x += dx; newBox.y += dy;

        newBox.w -= dx; newBox.h -= dy;

      } else if(currentResizeHandle === "tr") {

        newBox.y += dy;

        newBox.w += dx; newBox.h -= dy;

      } else if(currentResizeHandle === "bl") {

        newBox.x += dx;

        newBox.w -= dx; newBox.h += dy;

      } else if(currentResizeHandle === "br") {

        newBox.w += dx; newBox.h += dy;

      }

      

      // Prevent negative width/height.

      if(newBox.w < 5 || newBox.h < 5) return;

      

      // Update selected shape's properties based on its type.

      if(selectedShape.type === "rect" || selectedShape.type === "line") {

        // For these, map new bounding box to start and end.

        selectedShape.props.start = { x: newBox.x, y: newBox.y };

        selectedShape.props.end = { x: newBox.x + newBox.w, y: newBox.y + newBox.h };

      } else if(selectedShape.type === "circle") {

        // For circle, start remains the center.

        selectedShape.props.start = { x: newBox.x + newBox.w/2, y: newBox.y + newBox.h/2 };

        // Use average of width and height as radius.

        const r = (newBox.w + newBox.h) / 4;

        selectedShape.props.end = { x: selectedShape.props.start.x + r, y: selectedShape.props.start.y };

      } else if(selectedShape.type === "free") {

        // Scale free drawing points relative to original bounding box.

        const scaleX = newBox.w / origBox.w;

        const scaleY = newBox.h / origBox.h;

        selectedShape.props.points = originalProps.points.map(pt => {

          return {

            x: newBox.x + (pt.x - origBox.x) * scaleX,

            y: newBox.y + (pt.y - origBox.y) * scaleY

          };

        });

      }

      redraw();

      return;

    }

    

    // If in select mode and moving an object (and not resizing), move the shape.

    if(currentTool === "select" && selectedShape && !resizing) {

      // Calculate movement based on last mouse position (here we simply use delta movement)

      // For simplicity, we update by the difference from previous event position.

      // (A more robust solution would store previous coordinates.)

      selectedShape.move(0, 0); // placeholder if needed

      // In this simple demo, moving is handled by clicking and dragging on the shape body (not on handles).

    }

  });

  

  canvas.addEventListener("mouseup", (e) => {

    drawing = false;

    if(currentShape && currentTool !== "select") {

      frames[currentFrameIndex].shapes.push(currentShape);

      currentShape = null;

      redraw();

    }

    if(currentTool === "select" && resizing) {

      resizing = false;

      currentResizeHandle = null;

      originalProps = null;

    }

  });

  

  /* ========= Playback Functionality ========= */

  let playbackInterval = null;

  function playbackFrames() {

    if(frames.length === 0) return;

    let cur = 0;

    const loop = document.getElementById("loopCheckbox").checked;

    if(playbackInterval) clearInterval(playbackInterval);

    playbackInterval = setInterval(() => {

      currentFrameIndex = cur;

      updateFrameSelect();

      redraw();

      cur++;

      if(cur >= frames.length) {

        if(loop) cur = 0;

        else {

          clearInterval(playbackInterval);

        }

      }

    }, 500); // 500ms per frame

  }

  

  /* ========= Toolbar Button Actions ========= */

  document.getElementById("freeDrawBtn").addEventListener("click", () => {

    currentTool = "free";

    updateToolDisplay();

  });

  document.getElementById("lineBtn").addEventListener("click", () => {

    currentTool = "line";

    updateToolDisplay();

  });

  document.getElementById("rectBtn").addEventListener("click", () => {

    currentTool = "rect";

    updateToolDisplay();

  });

  document.getElementById("circleBtn").addEventListener("click", () => {

    currentTool = "circle";

    updateToolDisplay();

  });

  document.getElementById("selectBtn").addEventListener("click", () => {

    currentTool = "select";

    updateToolDisplay();

  });

  // Delete the selected shape.

  document.getElementById("deleteBtn").addEventListener("click", () => {

    if(currentTool === "select" && selectedShape) {

      const shapes = frames[currentFrameIndex].shapes;

      const index = shapes.indexOf(selectedShape);

      if(index !== -1) {

        shapes.splice(index, 1);

      }

      selectedShape = null;

      redraw();

    }

  });

  

  document.getElementById("addFrameBtn").addEventListener("click", () => {

    addFrame();

  });

  

  document.getElementById("frameSelect").addEventListener("change", (e) => {

    selectFrame(Number(e.target.value));

  });

  

  document.getElementById("playbackBtn").addEventListener("click", () => {

    playbackFrames();

  });

  

  document.getElementById("gridCheckbox").addEventListener("change", () => {

    redraw();

  });

  

  /* ========= Initialization ========= */

  // Start with one empty frame.

  addFrame();

  updateToolDisplay();

  </script>

</body>

</html>


Comments

Popular posts from this blog

Earn Money From Google AdMob ( Make Money Online 2024 ) | PASSIVE INCOME 2024

Earn Passive Money From Google AdMob ( Make Money Online ) |  https://cpamarketingbeginners.blogspot.com/ Admob Earnings Earn Instantly Hundreds of dollars without doing hard work with Google AdMob. Google Ad Mob is a Google's Product. AdMob is A Just like AdSense. You can Earn Money From Your Website and YouTube Channel Using AdSense. But if you want to earn money from Your app and Game then you can Use AdMob.  Google Run Ads on Website, YouTube Channel, App and Game Application. when Someone Clicks on that ads then You can Earn money. You can Earn $1 - $5 only on one Click.  Earning depends on Traffic quality and Category. If you have made your website, YouTube Channel or App on Business category and If you are getting traffic from United states, United kingdom, Canada, Germany.... then you can earn high amount of money. In this article, I am going to share with you how you can earn money from admob. read this full article. Guys, First of all you will have to make game ...

CrakRevenue Tutorials for Beginners ( Earn Money Online in 2024 )

Best and High paying Crakrevenue Affiliate Network. (CrakRevenue Tutorials for Beginners) In this article, I'm going to share with you best crakrevenue earning methods. it's for completely beginners. You can start making money from today. i will also share with you my crakrevenue earnings . Read this full articles.  (CrakRevenue Tutorials for Beginners) First of all you will have to create account on crakrevenue affiliate network. after that you can promote high paying cpa offers. Crakrevenue cpa offers you can promote Jerkmate cpa offers. when someone will click on this Jerkmate cpa offer and sign up after that they will verify their email address then you will earn $7.50 per sign up. as you can see in the Image. this offer is DOI. That means Sign up + Confirm Email address. suppose if it will be SOI Offers then you just need to generate only Sign up. when someone will just sign up then you can earn. (CrakRevenue Tutorials for Beginners) Next step you will have to create ...

Submit Your Website on all Search Engines to get Traffic on your website

  Submit Your Website on all Search Engines to get Traffic on your website In this article, I'm going to give you 63 Big Search engines to submit your website. after submit your website on these Search engines your website can Instantly start to get lots of traffic. you will have to submit your website on all search engines. if you will not submit your website on search engines then your website will not show in Search results.  yandex search engines like when someone will try to search your website on Google then your website will not show in search result. you will have to submit your website on Google Console. similarly I have collected all search Engines to submit your website and get free lots of traffic. read this full articles. 63+ Search Engines that I have collected to submit your website and get over 500K free traffic instantly Today..   Check out all these search engines -  1 -  ASR (Active Search Results ) 2 -  Google 3-  Amidalla 4 - ...