drawing
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property real arcStartAngle: 0.75 * Math.PI
|
||||
readonly property real arcSweep: 1.5 * Math.PI
|
||||
property real currentHue: 0
|
||||
required property var drawing
|
||||
property real handleSize: 30
|
||||
property real lastChromaticHue: 0
|
||||
property real ringThickness: 12
|
||||
readonly property int segmentCount: 180
|
||||
|
||||
function hueToAngle(hue) {
|
||||
return arcStartAngle + arcSweep * hue;
|
||||
}
|
||||
|
||||
function normalizeAngle(angle) {
|
||||
const tau = Math.PI * 2;
|
||||
let a = angle % tau;
|
||||
if (a < 0)
|
||||
a += tau;
|
||||
return a;
|
||||
}
|
||||
|
||||
function syncFromPenColor() {
|
||||
if (!drawing)
|
||||
return;
|
||||
|
||||
const c = drawing.penColor;
|
||||
|
||||
// QML color exposes HSL channels directly.
|
||||
// If the current color is chromatic, move the handle to that hue.
|
||||
// If it is achromatic (black/white/gray), keep the last useful hue.
|
||||
if (c.hslSaturation > 0) {
|
||||
currentHue = c.hslHue;
|
||||
lastChromaticHue = c.hslHue;
|
||||
} else {
|
||||
currentHue = lastChromaticHue;
|
||||
}
|
||||
|
||||
canvas.requestPaint();
|
||||
}
|
||||
|
||||
function updateHueFromPoint(x, y) {
|
||||
const cx = canvas.width / 2;
|
||||
const cy = canvas.height / 2;
|
||||
const dx = x - cx;
|
||||
const dy = y - cy;
|
||||
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const radius = (Math.min(canvas.width, canvas.height) - handleSize - 8) / 2;
|
||||
|
||||
if (distance < radius - 24 || distance > radius + 24)
|
||||
return;
|
||||
|
||||
const angle = normalizeAngle(Math.atan2(dy, dx));
|
||||
const start = normalizeAngle(arcStartAngle);
|
||||
|
||||
let relative = angle - start;
|
||||
if (relative < 0)
|
||||
relative += Math.PI * 2;
|
||||
|
||||
if (relative > arcSweep) {
|
||||
const gap = Math.PI * 2 - arcSweep;
|
||||
relative = relative < arcSweep + gap / 2 ? arcSweep : 0;
|
||||
}
|
||||
|
||||
currentHue = relative / arcSweep;
|
||||
lastChromaticHue = currentHue;
|
||||
drawing.penColor = Qt.hsla(currentHue, 1.0, 0.5, 1.0);
|
||||
}
|
||||
|
||||
implicitHeight: 180
|
||||
implicitWidth: 220
|
||||
|
||||
Component.onCompleted: syncFromPenColor()
|
||||
onCurrentHueChanged: canvas.requestPaint()
|
||||
onDrawingChanged: syncFromPenColor()
|
||||
|
||||
Connections {
|
||||
function onPenColorChanged() {
|
||||
root.syncFromPenColor();
|
||||
}
|
||||
|
||||
target: root.drawing
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: canvas
|
||||
|
||||
anchors.fill: parent
|
||||
renderStrategy: Canvas.Threaded
|
||||
renderTarget: Canvas.Image
|
||||
|
||||
Component.onCompleted: requestPaint()
|
||||
onHeightChanged: requestPaint()
|
||||
onPaint: {
|
||||
const ctx = getContext("2d");
|
||||
ctx.reset();
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
const cx = width / 2;
|
||||
const cy = height / 2;
|
||||
const radius = (Math.min(width, height) - root.handleSize - 8) / 2;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, root.arcStartAngle, root.arcStartAngle + root.arcSweep);
|
||||
ctx.lineWidth = root.ringThickness + 4;
|
||||
ctx.lineCap = "round";
|
||||
ctx.strokeStyle = Qt.rgba(1, 1, 1, 0.12);
|
||||
ctx.stroke();
|
||||
|
||||
for (let i = 0; i < root.segmentCount; ++i) {
|
||||
const t1 = i / root.segmentCount;
|
||||
const t2 = (i + 1) / root.segmentCount;
|
||||
const a1 = root.arcStartAngle + root.arcSweep * t1;
|
||||
const a2 = root.arcStartAngle + root.arcSweep * t2;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, a1, a2);
|
||||
ctx.lineWidth = root.ringThickness;
|
||||
ctx.lineCap = "round";
|
||||
ctx.strokeStyle = Qt.hsla(t1, 1.0, 0.5, 1.0);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
const handleAngle = root.hueToAngle(root.currentHue);
|
||||
const hx = cx + radius * Math.cos(handleAngle);
|
||||
const hy = cy + radius * Math.sin(handleAngle);
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(hx, hy, root.handleSize / 2, 0, Math.PI * 2);
|
||||
ctx.fillStyle = Qt.rgba(1, 1, 1, 0.95);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(hx, hy, root.handleSize / 2, 0, Math.PI * 2);
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.strokeStyle = Qt.rgba(0, 0, 0, 0.18);
|
||||
ctx.stroke();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(hx, hy, root.handleSize / 2 - 6, 0, Math.PI * 2);
|
||||
ctx.fillStyle = root.drawing.penColor;
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(hx, hy, root.handleSize / 2 - 6, 0, Math.PI * 2);
|
||||
ctx.lineWidth = 1;
|
||||
ctx.strokeStyle = Qt.rgba(0, 0, 0, 0.20);
|
||||
ctx.stroke();
|
||||
}
|
||||
onWidthChanged: requestPaint()
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
anchors.fill: parent
|
||||
|
||||
onPositionChanged: mouse => {
|
||||
if (mouse.buttons & Qt.LeftButton)
|
||||
root.updateHueFromPoint(mouse.x, mouse.y);
|
||||
}
|
||||
onPressed: mouse => root.updateHueFromPoint(mouse.x, mouse.y)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user