Estoy trabajando en un html canvas y estoy dibujando algunas líneas en el lienzo. El proceso es fácil. pero ahora quiero dibujar dos líneas en las mismas coordenadas con diferentes anchos, pero una en el lado derecho de las coordenadas de la línea real y otra en el lado izquierdo. Para ser más claro, veamos este ejemplo https://jsfiddle.net/am2222/2oqhLfd9/
Como puede ver, cuando dibujamos una línea con un ancho de digamos x , se extenderá x/2 píxeles alrededor de la línea. Quiero obtener algo como esto: 
como puede ver, el ancho de la línea del segundo ejemplo se extiende en un solo lado. Sé que puedo obtener esto compensando la línea x/2 en un lado, pero no estoy seguro de cómo funcionarían los cálculos en el lienzo. PD: quiero obtener algo como esto al final. Dos líneas con diferentes estilos con las mismas coordenadas 
En este ejemplo tomo un enfoque alternativo. Creo una imagen SVG, la cargo en un objeto de imagen y luego la dibujo en el lienzo. Por supuesto, el SVG podría estar oculto/solo una cadena en JS.
No sé sobre el rendimiento de esto, por lo que tal vez no funcione en su contexto; solo encuentro que es una forma divertida de resolver el problema.
var svg = document.querySelector('svg'); var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); var img = new Image(300, 30); img.addEventListener('load', e => { ctx.drawImage(e.target, 0, 0); }); var imagesrc = btoa(svg.outerHTML); img.src = `data:image/svg+xml;base64,${imagesrc}`; <svg viewBox="0 0 200 20" width="300" height="30" xmlns="http://www.w3.org/2000/svg"> <defs> <linearGradient id="lg1" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="orange" /> <stop offset="1" stop-color="red" /> </linearGradient> <linearGradient id="lg2" gradientUnits="userSpaceOnUse"> <stop offset="0" stop-color="navy" /> <stop offset="1" stop-color="darkgreen" /> </linearGradient> </defs> <line x1="0" y1="5" x2="200" y2="5" stroke-width="10" stroke="url(#lg1)" /> <line x1="0" y1="15" x2="200" y2="15" stroke-width="10" stroke="url(#lg2)" /> <line x1="0" y1="10" x2="200" y2="10" stroke-width="2" stroke="black" /> </svg> <canvas width="300" height="30"></canvas>Esto podría ayudar. Funcionalicé el dibujo de la línea de degradado con una bandera para dibujar el subrayado negro.
La función toma coordenadas, ancho de línea y color para degradados.
Este podría ser un punto de partida para explorar. Puede haber casos extremos que me perdí en los que esta función fallará.
var can = document.getElementById('canvas1'); var ctx = can.getContext('2d'); function distance( a, b ) { return Math.sqrt((ax - bx) * (ax - bx) + (ay - by) * (ay - by) ); } function drawGradientLine( line_width, grad_1, grad_2, xy1, xy2, underline ) { var grad = ctx.createLinearGradient(xy1.x, xy1.y, xy2.x, xy2.y); grad.addColorStop(0, grad_1); grad.addColorStop(1, grad_2); ctx.save(); ctx.lineWidth = line_width; ctx.strokeStyle = grad; ctx.beginPath(); ctx.moveTo(xy1.x, xy1.y); ctx.lineTo(xy2.x, xy2.y); ctx.stroke(); ctx.restore(); if ( underline ) { const linelen = distance( xy1, xy2); const hyp1 = line_width / 2; const angle = Math.asin( (xy2.y - xy1.y) / (linelen) ); const dy = (angle < 0) ? -1 * hyp1 * Math.cos( angle ) : hyp1 * Math.cos( angle ); const dx = (angle < 0) ? -1 * hyp1 * Math.sin( angle ) : hyp1 * Math.sin( angle ); const c1 = { x: xy1.x - dx, y: xy1.y + dy }; const c2 = { x:xy2.x - dx, y: xy2.y + dy }; ctx.save(); ctx.lineWidth = 1; ctx.strokeStyle = 'black'; ctx.beginPath(); ctx.moveTo(c1.x, c1.y); ctx.lineTo(c2.x, c2.y); ctx.stroke(); ctx.restore(); } } drawGradientLine( 20, 'red', 'green', {x:50, y: 150}, {x:150, y:150}, true); drawGradientLine( 20, 'pink', 'orange', {x:10, y: 10}, {x:50, y:50}, true); drawGradientLine( 20, 'pink', 'orange', {x:200, y: 100}, {x:250, y:20}, true); drawGradientLine( 10, 'blue', 'green', {x: 50, y: 350}, {x:150, y:450}, false); drawGradientLine( 40, 'pink', 'aquamarine', {x: 100, y: 200}, {x: 150, y:350}, false); <canvas id=canvas1 width=300 height=600></canvas>Solo es cuestión de moverse por el lienzo y dibujar líneas. Aquí estoy dibujando tres líneas, dos de ellas con degradado y una en negro.
Una alternativa a las líneas podría ser CanvasRenderingContext2D.fillRect() donde define x, y, ancho y alto. Entonces es más fácil calcular dónde deberían estar las líneas.
var canvas = document.querySelector('canvas'); var ctx = canvas.getContext('2d'); let gradient1 = ctx.createLinearGradient(0, 0, 280, 0); gradient1.addColorStop(0, 'green'); gradient1.addColorStop(1, 'blue'); ctx.strokeStyle = gradient1; ctx.lineWidth = 10; ctx.beginPath(); ctx.moveTo(10, 10); ctx.lineTo(290, 10); ctx.stroke(); let gradient2 = ctx.createLinearGradient(0, 0, 280, 0); gradient2.addColorStop(0, 'red'); gradient2.addColorStop(1, 'green'); ctx.strokeStyle = gradient2; ctx.lineWidth = 10; ctx.beginPath(); ctx.moveTo(10, 20); ctx.lineTo(290, 20); ctx.stroke(); ctx.strokeStyle = 'black'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(10, 15); ctx.lineTo(290, 15); ctx.stroke(); <canvas width="300" height="200"></canvas>