<script src="https://unpkg.com/[email protected]/konva.min.js"></script>


<input id="slider" type="range" min="1" max="100" value="50">
  
<div id="container"></div>
      body {
        margin: 40px;
        padding: 20px;
        overflow: hidden;
        background-color: #f0f0f0;
      }
div {
  margin-top: 10px; 
}
 
// This is info to control drawing the stars
const info = {
  count: 5,  // how many stars
  points: 5, // how many points on the stars - usually 5 !
  outerRadius: 40,  // outer radius of star 
  innerRadius: 20,  // inner radius of star
  position: {x: 40, y: 40},  // center of first star
  gap: 80,  // gap between stars
  percent: 100,   // percentage rating value
  colorInfoNeutral: {fill: 'silver'},
  colorInfoPositive: {
    fill: null,  // no solid color for this one.
    fillLinearGradientStartPoint: { x: 0, y: 0 },
    fillLinearGradientEndPoint: { x: 200, y: 200 },
    fillLinearGradientColorStops: [0, 'red', 1, 'yellow']       
  }
}

// calculate the max width for the background racts
info.maxWidth = (info.innerRadius + info.gap) * (info.count - 1);

// create a stage & layer for drawing
const stage = new Konva.Stage({
    container: "container", // id of container <div>
    width: 500,
    height: 500
  }),
  layer = new Konva.Layer(),
  starData = []; // array of points on the stars

// Function to calculate points of stars - we need these for the clip function so do not use Konva Star here,
// though this is the basis of the code in the Konva source that draws stars.
function starCalc(pos, numPoints, outerRadius, innerRadius ){
  const data = [];
 
  data.push({x: pos.x, y: pos.y - outerRadius})
  for (let n = 1; n < numPoints * 2; n++) {
    const radius = n % 2 === 0 ? outerRadius : innerRadius,
          x = radius * Math.sin((n * Math.PI) / numPoints),
          y = -1 * radius * Math.cos((n * Math.PI) / numPoints);
    data.push({x: pos.x + x, y: pos.y + y});
  }
  data.push({x: data[0].x, y: data[0].y})
  return data;
}

// Now we use the star info to call the function to calculate the star points - no drawing yet ! 
for (let i = 0; i < info.count; i++){    
  const startPoints = starCalc({x: info.position.x + (i * info.gap), y: info.outerRadius}, info.points, info.outerRadius, info.innerRadius);
  starData.push(startPoints);
}

// Now create a group to contain the stars and the background rects.
// Important! - We create the clipping function using the star points - the stars become cutouts.
const group = new Konva.Group({
  draggable: true,
  
   clipFunc: function (ctx) {
      // Start only one path
      ctx.beginPath();

      for (let i = 0; i < starData.length; i++){
        ctx.moveTo(starData[i][0].x, starData[i][0].y)
        for (let j = 1; j < starData[i].length; j++){
          ctx.lineTo(starData[i][j].x, starData[i][j].y)
        }
        // Closing path, but not starting a new one
        ctx.closePath();
      }  
   }
});

// make the background recs - we need 2 to show the different colours
const bgRect1 = new Konva.Rect({
  width: info.maxWidth,
  height: info.outerRadius * 2,
})
bgRect1.setAttrs(info.colorInfoNeutral);

const bgRect2 = bgRect1.clone(info.colorInfoPositive);

// add the bg rects to the group - the positive color rect is on top, grey underneath
group.add(bgRect1, bgRect2);
  
// add the group to the layer
layer.add(group);

// add the layer to the stage
stage.add(layer);


// A function to set the display correctly for the given rating, in percent 0 - 100
function setRatingPercent(percent){
  info.percent = percent;
  bgRect2.width(info.maxWidth *  info.percent / 100);
}

// Listener for the slider
$('#slider').on('input change', function() {
  setRatingPercent(this.value);
});

// Inially set the percentage to match the slider.
setRatingPercent(50);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js