Search code examples
htmlcsssvghtml5-canvascss-shapes

Creating a 'New' spiky label with 24 or above point burst


I am trying to make a point burst thing like the image below:

enter image description here

Currently, I have tried this using pseudo elements, however, I was only able to generate a 12 point burst and does not reflect that which is displayed within the image.

Is there anyway to create a point burst with only a few elements?

Below is the code I have used to attempt this solution:

div{
    width:100px;
    height:100px;
    background:grey;
    transform:rotate(45deg);
    margin:50px;
}
div:after{
    position:absolute;
    content:"";
    background:grey;
    width:100px;
    height:100px;
    transform:rotate(135deg);
}
div:before{
    position:absolute;
    content:"";
    background:grey;
    width:100px;
    height:100px;
    transform:rotate(250deg);
}
<div></div>


Solution

  • Canvas Approach

    You can also achieve this using Canvas. The commands for drawing on Canvas are pretty much the same as in SVG. The approach, on a very high level, would be to find points on two circles (one with radius as x and another with a slightly smaller radius) and then connect them together to form a path. When the path is filled, it gives the appearance of a n-point burst.

    In the below diagram, the green circle is the bigger circle with radius as x and the blue circle is the smaller inner circle. By plotting points on the circles and connecting them (with lineTo commands), we get the path which is in red. When this path is filled we get the burst appearance. (Note: The inner and outer circles are only for illustration and are not drawn in the actual diagram).

    enter image description here


    Calculation Logic

    • The X and Y coordinates of each points on the circle can be calculated using the below formula:
      • x = (Radius of circle * Cos(Angle in Radians)) + x coordinate of center
      • y = (Radius of circle * Sin(Angle in Radians)) + y coordinate of center
    • The angle at which the points are plotted on the circle are determined using the below logic:
      • As used in both your and Persijn's answers, the angle is calculated as (360/no. of bursts). 360 is used because it is the total angle within a circle.
      • Angle of the points on the inner circle should be half way between point1 and point2 on the larger circle and hence a delta is added to it. The delta is half of (360/no. of bursts)
    • Angle in Radians = Angle in Degrees * π / 180

    window.onload = function() {
      var canvas = document.getElementById('canvas');
      var ctx = canvas.getContext('2d');
      var defaultBurst = 18;
      var defaultContent = "New";
    
      function paint(numBurst, text) {
        if (!numBurst) numBurst = defaultBurst;
        if (!text) text = defaultContent;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = 'crimson';
        var angleInRad = Math.PI * (360 / numBurst) / 180;
        var deltaAngleInRad = angleInRad / 2;
        ctx.beginPath();
        ctx.moveTo(75, 150);
        for (i = 0; i <= numBurst; i++) {
          x1 = 75 * Math.cos(angleInRad * i) + 150;
          y1 = 75 * Math.sin(angleInRad * i) + 150;
          x2 = 60 * Math.cos((angleInRad * i) + deltaAngleInRad) + 150;
          y2 = 60 * Math.sin((angleInRad * i) + deltaAngleInRad) + 150;
          ctx.lineTo(x1, y1);
          ctx.lineTo(x2, y2);
        }
        ctx.closePath();
        /* Add shadow only for shape */
        ctx.shadowOffsetX = -5;
        ctx.shadowOffsetY = 5;
        ctx.shadowBlur = 5;
        ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
        ctx.fill();
        ctx.font = "32px Arial";
        ctx.textAlign = "center";
        ctx.fillStyle = "gold";
        /* Nullify shadow for text */
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
        ctx.fillText(text, 150, 160, 120);
      }
      paint();
      var slider = document.getElementById('burst');
      var textInput = document.getElementById('content');
      slider.addEventListener('change', function() {
        paint(this.value, textInput.value);
      });
    
      textInput.addEventListener('blur', function() {
        paint(slider.value, this.value);
      });
    }
    /* For demo only */
    
    .controls {
      float: right;
      padding: 5px;
      margin: 50px 20px;
      line-height: 25px;
      border: 1px solid;
      box-shadow: 1px 1px 0px #222;
    }
    label,
    input {
      display: inline-block;
      vertical-align: middle;
      text-align: left;
    }
    h3 {
      padding: 10px;
      text-align: center;
    }
    label {
      width: 100px;
    }
    input[type='range'],
    input[type='text'] {
      width: 100px;
    }
    body {
      font-family: Calibri;
      background-image: radial-gradient(circle, #3F9CBA 0%, #153346 100%);
    }
    <canvas id='canvas' height='300px' width='300px'></canvas>
    <div class='controls'>
      <h3>Controls</h3>
    
      <label for="burst">Change Burst:</label>
      <input id="burst" class="slider" type="range" value="18" min="12" max="36" step='2' title="Adjust slider to increase or decrease burst" />
      <br/>
      <label for="content">Text Content:</label>
      <input type="text" id="content" maxlength="5" />
    </div>


    Advanced Demo

    Check out this CodePen for an advanced demo with features like path creation animation, shadows, control over all the features etc.


    Usage Advice

    If you want a fixed size image somewhere in the page then Canvas is as good as SVG. However, if you would need an image that can be scaled to any size, Canvas is not the right choice because Canvas is raster based and becomes pixelated or blurred when scaled.

    If your shape would need a dynamic number of bursts and/or text, Canvas would be more preferable over SVG and CSS because you don't have to perform any DOM manipulations.