Adjust canvas size based on chosen option

While experimenting with canvas, I had an interesting idea about altering the dimensions of a cube. Using HTML5 Canvas, I created a representation of a cube consisting of two squares connected by lines to form a 3D shape.

My goal is to have the ability to select different cube types from a dropdown menu, which would dynamically adjust the cube's size based on the selected dimensions while keeping the height constant. For example, if I choose a 5x5 cube, the default cube shape remains; but if I select 5x10, only the length changes while maintaining the width, and vice versa for 10x5 where the width expands. The maximum option available is 25x15. Furthermore, the pixel measurements in the canvas need to be converted to centimeters and then into cubic meters.

The entire cube should be positioned within the specified fixed canvas area.

View the demo

var canvas = document.querySelector('canvas');

canvas.width = 500;
canvas.height = 300;

var contxt = canvas.getContext('2d');

//squares
/*
contxt.fillRect(x, y, widht, height);
*/
contxt.strokeStyle = 'grey';
var fillRect = false;
contxt.fillStyle = 'rgba(0, 0, 0, 0.2)';
contxt.rect(80, 80, 100, 100);
contxt.rect(120, 40, 100, 100);
if (fillRect) {
  contxt.fill();
}
contxt.stroke();

/*Lines
contxt.beginPath();
contxt.moveTo(x, y);
contxt.lineTo(300, 100);
*/
contxt.beginPath();

contxt.moveTo(80, 80);
contxt.lineTo(120, 40);

contxt.moveTo(180, 80);
contxt.lineTo(220, 40);

contxt.moveTo(80, 180);
contxt.lineTo(120, 140);

contxt.moveTo(180, 180);
contxt.lineTo(220, 140);

contxt.stroke();
canvas {
  border: 1px solid #000;
}
select {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select>
  <option>5x5</option>
  <option>5x10</option>
  <option>10x5</option>
</select>

<canvas></canvas>

Answer №1

Creating a Cube Drawing Function:

If you want to generate a dynamic cube, you need to listen for onChange events on the <select> element. Whenever the selected option changes, you should redraw your cube.

To redraw the cube, you must create a function called renderCube. This function should take the new dimensions of the cube and an offset for positioning. Within this function, clear the previously drawn cube and redraw the new one with the specified dimensions and offset.

Implementing a Transition Effect:

Since you cannot apply CSS transitions to canvas elements directly, you have to implement the transition yourself. Create an animation function that calculates the dimensions of the cube during the transition phase and updates it on each frame.

A Resizable Cube Implementation with Transition Effect Would Look Like:
(Check out this fiddle as well)
(If you don't need the transition effect, view the fiddle before its implementation)

var canvas = document.querySelector('canvas');
canvas.width = 320;
canvas.height = 150;
var context = canvas.getContext('2d');

var currentHeight = 0, currentWidth = 0, currentDepth = 0, animationId = 0;

function renderCube(height, width, depth, offsetX, offsetY) {
  currentHeight = height;
  currentWidth = width;
  currentDepth = depth;

  // Clear any existing cube
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.beginPath();

  // Calculate depth, width, and height based on the given input
  depth = (depth * 10 * 0.8) / 2;
  width = width * 10;
  height = height * 10;

  // Draw two squares on the canvas
  context.strokeStyle = 'grey';
  var fillRect = false;
  context.fillStyle = 'rgba(0, 0, 0, 0.2)';
  context.rect(offsetX, offsetY, width, height);
  context.rect(offsetX + depth, offsetY - depth, width, height);
  if (fillRect) {
    context.fill();
  }
  context.stroke();

  // Define coordinates for drawing depth lines between the two rectangles
  var depthLineCoordinates = [
    [0, 0, depth, -depth],
    [width, 0, width + depth, -depth],
    [0, height, depth, height - depth],
    [width, height, width + depth, height - depth]
  ];

  // Draw the depth lines on the canvas
  depthLineCoordinates.forEach(function(element) {
    context.moveTo(offsetX + element[0], offsetY + element[1]);
    context.lineTo(offsetX + element[2], offsetY + element[3]);
  });
  context.stroke();
}

// Example of adding a transition effect to the cube
function renderCubeWithTransition(height, width, depth, offsetX, offsetY, transitionDuration) {
  var fps = 60;
  var then = Date.now();
  var startTime = then;
  var finished = false;

  var heightDifference = (height - currentHeight);
  var widthDifference = (width - currentWidth);
  var depthDifference = (depth - currentDepth);

  var transitionStartMillis = (new Date()).getMilliseconds();
  animationId = transitionStartMillis;

  function animate() {
    if (transitionStartMillis != animationId) return;
    
    if (!finished) requestAnimationFrame(animate);

    now = Date.now();
    elapsed = now - then;

    if(elapsed > (1000 / fps)) {
      then = now - (elapsed % (1000 / fps));

      if(parseInt(currentHeight, 0) != parseInt(height, 0)) currentHeight += heightDifference / (transitionDuration * fps);
      
      if(parseInt(currentWidth, 0) != parseInt(width, 0)) currentWidth += widthDifference / (transitionDuration * fps);
      
      if(parseInt(currentDepth, 0) != parseInt(depth, 0)) currentDepth += depthDifference / (transitionDuration * fps);

      renderCube(currentHeight, currentWidth, currentDepth, offsetX, offsetY);

      if(parseInt(currentHeight, 0) === parseInt(height, 0) && parseInt(currentWidth, 0) === parseInt(width, 0) && parseInt(currentDepth, 0) === parseInt(depth, 0)) {
        finished = true;
      }
    }
  }

  animate();

  return true;
}

// Initial cube drawing with dimensions 5x5
renderCube(5, 5, 5, 80, 70);

// Add onChange event listener to the select element
var cubeSizeSelector = document.getElementById('cubeSizeSelector');
cubeSizeSelector.onchange = function(e) {
  var cubeSize = e.target.value.split('x');
  renderCubeWithTransition(5, parseInt(cubeSize[0], 0), parseInt(cubeSize[1], 0), 80, 70, 0.3);
}
canvas {
  border: 1px solid #000;
}
select {
  display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"> </script>
<select id="cubeSizeSelector">
  <option>5x5</option>
  <option>5x10</option>
  <option>10x5</option>
</select>

<canvas></canvas>

Answer №2

Creating a 3D outline using Axonometric projection

An ideal approach is to develop a general axonometric rendering tool that can take a floor plan and display the object on the canvas as required.

You can then link the plan to a selection box and update the view whenever the selection changes.

Illustrated through code

In the code example below, the renderIsoPlan object is utilized to visualize the shape.

The shapes are defined based on a plan. For instance, a box has a floor plan represented by the four bottom corners in the format [[-1,-1],[1,-1],[1,1],[-1,1]].

The renderIsoPlan object consists of the following properties:

  • canvas: The canvas where the shape will be rendered. It won't display until this is specified. If you already have a 2D context created, it will be reused.
  • height: Indicates how far up the outline should be projected.
  • style: An object containing canvas context styles, for example
    {strokeStyle : "red", lineWidth : 2}
    to draw red lines with 2 pixels width.
  • plan: Array of points representing the floor. Points are automatically centered. For instance, [[0,-1],[1,1],[-1,1]] draws a triangle.
  • scale: Represents the scaling factor.
  • rotate: Amount to rotate. If non-zero, a different projection is applied (dimetric or trimetric).
  • centerY, centerX: Center position on the canvas in unit size. For example, 0.5 denotes center.

Use renderIsoPlan.refresh() to render the shape.

Note: Rotating the projection can lead to visual distortion, so if rotation is enabled, a different projection method is used.

Note: The object is automatically centered around 0,0; use centerX and centerY properties to adjust the centering within the view.

setTimeout(start,0); // wait till Javascript parsed and executed
requestAnimationFrame(animate); // Animate checked at start so start anim

// named list of shapes
const boxes = {
  box1By1: {
    plan: [[-1,-1],[1,-1],[1,1],[-1,1]],
    scale: 35,
    centerY: 0.75,
  },
  ...
}
...
// Renders animated object
function animate(time){     
  if (animateCheckBox.checked) {
    renderIsoPlan.rotate = time / 1000;
    renderIsoPlan.refresh();
    requestAnimationFrame(animate);
  }
}
canvas { border : 2px solid black; }
<select id="boxShape">
 <option value="box1By1">1 by 1</option>
 <option value="box1By2">1 by 2</option>
 <option value="box2By2">2 by 2</option>
 <option value="box2By1">2 by 1</option>
 <option value="box1By3">1 by 3</option>
 <option value="box1By4">1 by 4</option>
 <option value="lShape">L shape</option>
</select>
<input type="checkBox" id="animateCheckBox" checked=true>Animate</input><br>
<canvas id="canvas"></canvas>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way to retrieve the value of an HTML variable using Selenium?

Seeking assistance in determining the progress value of a video using Selenium. I understand that I need to utilize JavaScript executor, but have been unsuccessful in finding a solution so far. The element in question is: <div aria-label="scrub bar" a ...

Form submission caused by automatic loading of the page results in an endless loop

https://i.sstatic.net/cg5b3.jpgi'm currently implementing a Filter with the use of a Form. The desired functionality is that when a user lands on the list page, the form should automatically submit so that the user can view only the values they want t ...

What methods can I use to locate the datetime format within an HTML document using JavaScript?

I am working on a page where I need to locate and convert all datetime values. Specifically, I am looking to identify Hijri datetime values and convert them to standard datetimes using JavaScript. Could someone please advise me on how to locate datetime ...

Tips for styling your Angular Material Card on mobile devices

Currently, I am very happy with my desktop layout which looks like this: https://i.stack.imgur.com/tG0pw.png However, when it comes to the mobile version of my site, here is what I have so far: https://i.stack.imgur.com/KD1hh.jpg While I do like the ho ...

Issue with displaying ChartJS on Django HTML page

I am attempting to create a horizontal bar chart in HTML, but unfortunately, it is not displaying correctly. I have sent 2 variables from views.py, which are {{top5StockCode}} and {{top5TotalSales}}. The values of {{top5StockCode}} that were sent from vi ...

Access a website from a different domain and include JavaScript code dynamically

My goal is to develop a mobile-friendly web app that loads a website from a different domain and applies JavaScript code to adjust the layout. I have heard that JavaScript typically does not allow cross-domain requests, so I'm wondering how this could ...

Styles in print CSS are not effective in an Angular project

Currently, I am working on an Angular project where I need to generate a printable document using CSS. The challenge I am facing is ensuring that the date and title in the header do not print automatically when the document spans multiple pages. Additional ...

``How can I apply styling to a child component in React within a regular HTML element?

I'm currently learning React and faced a unique situation while working on projects. Here's the scenario - I have two React child components nested under a standard HTML element. If I need to target and style each individual React child element ...

Activate a CSS class on click using JavaScript

Having a bit of trouble as a beginner with this. Any help would be much appreciated. This is the code in question: HTML: <div class='zone11'> <div class='book11'> <div class='cover11'></d ...

Highlighted option selection in Material UI dropdown using Cypress

Can someone explain how to select Material-UI dropdown options using Cypress? I'm looking for a simple explanation, thanks! ...

How can I determine whether a value is present in a data attribute using jQuery?

I'm working with a HTML div that looks like this: <div id="myDiv" data-numbers="1 4 5 3 9 88 57 "></div> Within the data-numbers attribute of the div, there are random numbers separated by white space. My goal is to determine whether ...

jqxChart displaying data with proportions

Exploring the waterfall series feature of the jqxChart was quite interesting. As per its API, the code snippet below is used to set the values of the y-axis: valueAxis: { title: {text: 'Population<br>'}, unitInterval: 1000000, ...

The issue persists with the ajax.reload() function in DataTables

It's been driving me crazy that my datatables table won't refresh, despite using ajax.reload. I've spent weeks on this code but still can't get it to work. DataTablesDraw = (selector, order, pages, file, sort, column, template, data_se ...

How come Font-face isn't functioning properly on Internet Explorer 11?

I have designed a new website at , however, I am facing issues with my CSS on Internet Explorer 11 (version 11.608.15063.0). The font-face and dropdown menu are not displaying correctly. Can anyone assist me with this problem? ...

Seeking assistance in identifying the highest value in JavaScript (excluding the use of the max method)

Greetings and thank you for stopping by. I'm new to Javascript and currently facing an issue that I could use some help with. I am trying to identify the highest number from a set of variables using conditional statements within a function, but I seem ...

What is the best way to include specific script tags within the <head> and <body> sections when utilizing HtmlWebpackPlugin?

I am currently utilizing HtmlWebpackPlugin in order to create HTML files that include JavaScript. Now, I am interested in inserting custom scripts at various locations within the <head> and <body> tags For instance: How can I, Insert <s ...

What are some ways I can customize the appearance of form validation messages?

<input type="text" class="form-control " name="username" placeholder="Enter ID " required > I need to update the validation message position to display on the right side of the textbox and modify it to say "Enter Username". What is the best way to ...

EJS is failing to render

I am currently working on a rock, paper, scissors game using Node.js on the backend with an express server, frontend.js on the client-side, and index.ejs and main.css files. My goal is to display the result of the player's decision (win, lose, or draw ...

JQuery not properly validating inputs with required attributes after creation

I am currently developing a contact form that includes an "alternative address" <div id='alt'> with toggleable visibility. Inside this div, there is a required <input> field. I encountered an issue where toggling #alt twice (thus hidi ...

A guide on extracting href containing ampersands using Gatling

When encountering a link with ampersands in the URL during a Gatling stress test, issues may arise due to Gatling interpreting it as an entity. What can be done to work around this? For instance: Let's say you come across a webpage that contains &l ...