Yes, using DrawContext, .strokeEllipse etc.
Here’s a reimplementation of the circular range widget (left) with the temperature widget (right). Transparent masking doesn’t seem possible with DrawContext so that would require another approach.
function circularRange(val, min, max, width, height) {
let context = new DrawContext()
context.size = new Size(width, height)
context.respectScreenScale = true
context.opaque = false
// background
let rect = new Rect(0, 0, width, height)
context.setFillColor(Color.black())
context.fillRect(rect)
// ring
let lineWidth = 16
context.setLineWidth(lineWidth)
let ring = new Rect(lineWidth/2, lineWidth/2, width - lineWidth, height - lineWidth)
context.setStrokeColor(Color.gray())
context.strokeEllipse(ring)
// mask
let path = new Path()
path.move(new Point(width / 2, height / 2))
let end = Math.PI / 6
let my = ((1 + Math.tan(end)) / 2) * height
path.addLine(new Point(0, my))
path.addLine(new Point(0, height))
path.addLine(new Point(width, height))
path.addLine(new Point(width, my))
context.addPath(path)
context.setFillColor(Color.black())
context.fillPath()
// rounded ends
let lx = ((1 - Math.cos(end)) / 2) * (width - lineWidth)
let ly = ((1 + Math.sin(end)) / 2) * (height - lineWidth)
let lend = new Rect(lx, ly, lineWidth, lineWidth)
let rx = ((1 + Math.cos(end)) / 2) * (width - lineWidth)
let rend = new Rect(rx, ly, lineWidth, lineWidth)
context.setFillColor(Color.gray())
context.fillEllipse(lend)
context.fillEllipse(rend)
// value
let a = (195 - (val - min) / (max - min) * 210) * Math.PI / 180
let vx = ((1 + Math.cos(a)) / 2) * (width - lineWidth)
let vy = ((1 - Math.sin(a)) / 2) * (height - lineWidth)
let value = new Rect(vx, vy, lineWidth, lineWidth)
context.setLineWidth(lineWidth * 3/4)
context.setStrokeColor(Color.black())
context.strokeEllipse(value)
context.setFillColor(Color.white())
context.fillEllipse(value)
// labels
context.setTextColor(Color.white())
context.setFont(Font.mediumSystemFont(72))
context.setTextAlignedCenter()
context.drawTextInRect(val.toString(), new Rect(width/4, height/4, width/2, height/2))
context.setFont(Font.regularSystemFont(32))
context.setTextAlignedLeft()
context.drawTextInRect(min.toString(), new Rect(width/5, height * 7/10, width * 3/10, height * 3/10))
context.setTextAlignedRight()
context.drawTextInRect(max.toString(), new Rect(width/2, height * 7/10, width * 3/10, height * 3/10))
return context
}
let widget = new ListWidget()
widget.setPadding(0, 0, 0, -8)
let image = circularRange(16, 6, 18, 175, 175).getImage()
widget.addImage(image)
if (config.runsInAccessoryWidget) {
Script.setWidget(widget)
} else {
widget.presentSmall()
}