画布线性渐变采用另一个输入类型颜色字段的值

Posted

技术标签:

【中文标题】画布线性渐变采用另一个输入类型颜色字段的值【英文标题】:Canvas linear gradient taking the value of another input type color field 【发布时间】:2021-05-30 06:18:55 【问题描述】:

所以我有了这个简单的图像编辑器,我在其中使用画布绘制用户选择的图像和一些文本。也就是说,用户可以上传一张图片,然后如果他们愿意,他们可以添加文本或更改图片的渐变。

目前该应用程序运行良好,除了有一个问题。

如何找到问题?执行以下操作:

    上传随机图片。 从Text 2 Shadow Offset X 拾色器中选择一种颜色。 增加Text 2 Shadow Offset X 拾色器旁边的滑块。 然后增加Image Gradient and Opacity的滑块。

图像将具有渐变并采用Text 2 Shadow Offset X 颜色选择器的值,而不是默认黑色的Image Gradient and Opacity 颜色选择器。

这是代码:

const canvasTxt                 = window.canvasTxt.default;
const canvas                    = document.getElementById('canvas');
const ctx                       = canvas?.getContext('2d');
const btnDownload               = document.querySelector('.btnDownload');
const fileUpload                = document.querySelector('.file-upload');

const text1                     = document.getElementById('text1');
const textForm1                 = document.getElementById('text1-form');
const text2                     = document.getElementById('text2');
const textForm2                 = document.getElementById('text2-form');
const text2ShadowColor          = document.getElementById('text2shadowcolor');
const text2ShadowOffsetY        = document.getElementById('text2shadowoffy');

const imageForm                 = document.getElementById('image-form');
const imageGrad                 = document.getElementById('gradientcolor');
const imageGradOpacity          = document.getElementById('gradientopacity');


$(fileUpload).on('change', function(e) 
      let imgObj          = new Image();
      imgObj.onload       = draw;
      imgObj.onerror      = failed;
      imgObj.src          = URL.createObjectURL(this.files[0]);

      imgManipulation( e, imgObj );
);    

const imgManipulation = ( e, imgObj ) => 
    $(textForm1).on('change keyup input', updateCanvas);
    $(textForm2).on('change keyup input', updateCanvas);
    $(imageForm).on('change keyup input', updateCanvas);

    function updateCanvas(e) 
        e.preventDefault();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(imgObj, 0, 0);

        createGradient($(imageGrad).val(), $(imageGradOpacity).val());

  
        // TEXT1 STYLES based on user input
        canvasTxt.fontSize      = 30;
        canvasTxt.drawText(
            ctx, 
            $(text1).val(), 
            0, 
            0, 
            200, 
            200
        );


        // TEXT2 STYLES
        canvasTxt.font          = 20;

        canvasTxt.drawText(
            ctx, 
            $(text2).val(),
            20, 
            20, 
            200, 
            200
        );
    
;

function hexToRgb(hex) 
    var result = /^#?([a-f\d]2)([a-f\d]2)([a-f\d]2)$/i.exec(hex);
    return result ? 
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
     : null;
;

function createGradient(hex, alpha) 

    const r = hexToRgb(hex).r.toString();
    const g = hexToRgb(hex).g.toString();
    const b = hexToRgb(hex).b.toString();

    var gradient =  ctx.createLinearGradient(800, 0, 0, 0);
    gradient.addColorStop(0, `rgba($r, $g, $b, $alpha)`);

    ctx.save() // <----------- ADD
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.restore() // <----------- ADD
;


function draw() 
    canvas.width        = this.naturalWidth;
    canvas.height       = this.naturalHeight;
    const nw            = this.naturalWidth;
    const nh            = this.naturalHeight;

    ctx.drawImage(this, 0, 0, nw, nh);
;

function failed() 
    console.error("The provided file couldn't be loaded as an Image media");
;


$(btnDownload).on('click', function(e) 
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.href = canvas.toDataURL();
    a.download = "canvas-image.png";
    a.click();
    document.body.removeChild(a);
);    
#canvas
    background-color: transparent; 
    width: 30%; 
    height: auto;
    border: 1px solid #777;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://unpkg.com/canvas-txt@3.0.0/build/index.js"></script>

<canvas id="canvas"  ></canvas>

<div>
    <input type="file" class="file-upload" />
    <button class="btnDownload">Download</button>
</div>


<div>
  <form id="text1-form">
    <input type="text" id="text1" placeholder="text 1"/> 
  </form>
</div>

<div>
  <form id="text2-form">
    <input type="text" id="text2" placeholder="text 2"/> 
  </form>
</div>

<div>
  <h2>Image Gradient and Opacity</h2>
  <form id="image-form">
    <input type="color" id="gradientcolor" value="#000000" />
    <input type="range" id="gradientopacity" min="0" max="1" value="0" step="0.05" />
  </form>
</div>


<div>
  <h2>Text2 Shadow Offset X</h2>
  <input type="color" id="text2shadowcolor" value="#000000" />
  <input type="range" id="text2shadowoffy" min="0" max="40" value="0" />
</div>

代码概要:

1:首先我有fileUpload 事件监听器。它从用户那里获取图像并创建一个图像对象并将其绘制在画布上。然后以imgObjevent 作为参数调用imgManipulation 函数。

    imgManipulation 函数从文本的input 事件侦听器开始。也就是说,只要输入发生变化,即用户写了一些东西,就会调用updateCanvas函数。

    updateCanvas 函数实际上是在图像上绘制文本。我正在使用一个名为 canvasTxt 的包,它有助于将文本变为多行。

    updateCanvas 内部的createGradient 函数调用是图像的图像渐变。

我尝试将 createGradient 函数调用移动到 drawTexts 下方,但渐变出现在所有内容的顶部。文字也变暗了。

我怎样才能让渐变只取它的值而不是阴影颜色?

我们将非常感谢您的帮助。

提前致谢

【问题讨论】:

【参考方案1】:

这是因为由于您没有重置阴影选项,所以渐变本身会受到阴影的影响,就像它会受到过滤器的影响一样,如果它让您更清楚的话:

const ctx = canvas.getContext( "2d" );
// a vertical green to transparent gradient
const grad = ctx.fillStyle = ctx.createLinearGradient( 0, 0, 0, 150 );
grad.addColorStop( 0, "rgba(0,255,0)" );
grad.addColorStop( 1, "rgba(0,255,0,0.01)" );
ctx.textAlign = "center";

ctx.fillRect( 0, 0, 50, 150 );

ctx.filter = "sepia(1)";
ctx.fillRect( 75, 0, 50, 150 );

ctx.filter = "invert(1)";
ctx.fillRect( 150, 0, 50, 150 );

ctx.filter = "none"; // disable filtering

ctx.shadowColor = "red";
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 25;
ctx.fillRect( 225, 0, 50, 150 );

// disable shadows
ctx.shadowColor = "transparent";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;

ctx.fillStyle = "black";
ctx.fillText( "sepia(1)", 100, 20 );
ctx.fillText( "none", 25, 20 ); 
ctx.fillText( "invert(1)", 175, 20 );
ctx.fillText( "shadow", 250, 20 );
&lt;canvas id="canvas" width="500"&gt;&lt;/canvas&gt;

如您所见,由于您的渐变不是完全不透明的,阴影在其后面可见,并且渲染的颜色发生了变化。

要解决这个问题,您可以像我在示例中所做的那样:

// disable shadows
ctx.shadowColor = "transparent";
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;

const canvasTxt                 = window.canvasTxt.default;
const canvas                    = document.getElementById('canvas');
const ctx                       = canvas?.getContext('2d');
const btnDownload               = document.querySelector('.btnDownload');
const fileUpload                = document.querySelector('.file-upload');

const text1                     = document.getElementById('text1');
const textForm1                 = document.getElementById('text1-form');
const text2                     = document.getElementById('text2');
const textForm2                 = document.getElementById('text2-form');
const text2ShadowColor          = document.getElementById('text2shadowcolor');
const text2ShadowOffsetY        = document.getElementById('text2shadowoffy');

const imageForm                 = document.getElementById('image-form');
const imageGrad                 = document.getElementById('gradientcolor');
const imageGradOpacity          = document.getElementById('gradientopacity');


$(fileUpload).on('change', function(e) 
      let imgObj          = new Image();
      imgObj.onload       = draw;
      imgObj.onerror      = failed;
      imgObj.src          = URL.createObjectURL(this.files[0]);

      imgManipulation( e, imgObj );
);    

const imgManipulation = ( e, imgObj ) => 
    $(textForm1).on('change keyup input', updateCanvas);
    $(textForm2).on('change keyup input', updateCanvas);
    $(imageForm).on('change keyup input', updateCanvas);

    function updateCanvas() 
        e.preventDefault();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(imgObj, 0, 0);

        createGradient($(imageGrad).val(), $(imageGradOpacity).val());

  
        // TEXT1 STYLES based on user input
        canvasTxt.fontSize      = 30;
        canvasTxt.drawText(
            ctx, 
            $(text1).val(), 
            0, 
            0, 
            0, 
            0
        );


        // TEXT2 STYLES
        ctx.shadowColor         = $(text2ShadowColor).val();
        ctx.shadowOffsetY       = $(text2ShadowOffsetY).val();
        canvasTxt.font          = 20;

        canvasTxt.drawText(
            ctx, 
            $(text2).val(),
            20, 
            20, 
            0, 
            0
        );

        // clean behind
        ctx.shadowColor         = "transparent";
        ctx.shadowOffsetY       = 0;

    
;

function hexToRgb(hex) 
    var result = /^#?([a-f\d]2)([a-f\d]2)([a-f\d]2)$/i.exec(hex);
    return result ? 
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
     : null;
;

function createGradient(hex, alpha) 

    const r = hexToRgb(hex).r.toString();
    const g = hexToRgb(hex).g.toString();
    const b = hexToRgb(hex).b.toString();

    var gradient =  ctx.createLinearGradient(800, 0, 0, 0);
    gradient.addColorStop(0, `rgba($r, $g, $b, $alpha)`);

    const current_fill_style = ctx.fillStyle;
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    // clean behind
    ctx.fillStyle = current_fill_style;
;


function draw() 
    canvas.width        = this.naturalWidth;
    canvas.height       = this.naturalHeight;
    const nw            = this.naturalWidth;
    const nh            = this.naturalHeight;

    ctx.drawImage(this, 0, 0, nw, nh);
;

function failed() 
    console.error("The provided file couldn't be loaded as an Image media");
;


$(btnDownload).on('click', function(e) 
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.href = canvas.toDataURL();
    a.download = "canvas-image.png";
    a.click();
    document.body.removeChild(a);
);
#canvas
    background-color: transparent; 
    width: 30%; 
    height: auto;
    border: 1px solid #777;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://unpkg.com/canvas-txt@3.0.0/build/index.js"></script>

<canvas id="canvas"  ></canvas>

<div>
    <input type="file" class="file-upload" />
    <button class="btnDownload">Download</button>
</div>


<div>
  <form id="text1-form">
    <input type="text" id="text1" placeholder="text 1"/> 
  </form>
</div>

<div>
  <form id="text2-form">
    <input type="text" id="text2" placeholder="text 2"/> 
  </form>
</div>

<div>
  <h2>Image Gradient and Opacity</h2>
  <form id="image-form">
    <input type="color" id="gradientcolor" value="#000000" />
    <input type="range" id="gradientopacity" min="0" max="1" value="0" step="0.05" />
  </form>
</div>


<div>
  <h2>Text2 Shadow Offset X</h2>
  <input type="color" id="text2shadowcolor" value="#000000" />
  <input type="range" id="text2shadowoffy" min="0" max="40" value="0" />
</div>

【讨论】:

嘿伙计,很抱歉打扰您。但我需要基于此画布的其他帮助。现在,如果我上传一张图片并在上面写一些东西,它就可以正常工作。但是如果我想改变图像,那么之前写的文字就会消失。如何将文本保持在顶部并能够根据需要多次更改图像?【参考方案2】:

下次绘制“渐变”时,shadowColor 仍然有效。 在之前添加ctx.save(),在之后添加ctx.restore()

const canvasTxt                 = window.canvasTxt.default;
const canvas                    = document.getElementById('canvas');
const ctx                       = canvas?.getContext('2d');
const btnDownload               = document.querySelector('.btnDownload');
const fileUpload                = document.querySelector('.file-upload');

const text1                     = document.getElementById('text1');
const textForm1                 = document.getElementById('text1-form');
const text2                     = document.getElementById('text2');
const textForm2                 = document.getElementById('text2-form');
const text2ShadowColor          = document.getElementById('text2shadowcolor');
const text2ShadowOffsetY        = document.getElementById('text2shadowoffy');

const imageForm                 = document.getElementById('image-form');
const imageGrad                 = document.getElementById('gradientcolor');
const imageGradOpacity          = document.getElementById('gradientopacity');


$(fileUpload).on('change', function(e) 
      let imgObj          = new Image();
      imgObj.onload       = draw;
      imgObj.onerror      = failed;
      imgObj.src          = URL.createObjectURL(this.files[0]);

      imgManipulation( e, imgObj );
);    

const imgManipulation = ( e, imgObj ) => 
    $(textForm1).on('change keyup input', updateCanvas);
    $(textForm2).on('change keyup input', updateCanvas);
    $(imageForm).on('change keyup input', updateCanvas);

    function updateCanvas() 
        e.preventDefault();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(imgObj, 0, 0);

        createGradient($(imageGrad).val(), $(imageGradOpacity).val());

  
        // TEXT1 STYLES based on user input
        canvasTxt.fontSize      = 30;
        canvasTxt.drawText(
            ctx, 
            $(text1).val(), 
            0, 
            0, 
            0, 
            0
        );


        // TEXT2 STYLES
        ctx.save() // <----------- ADD
        ctx.shadowColor         = $(text2ShadowColor).val();
        ctx.shadowOffsetY       = $(text2ShadowOffsetY).val();
        canvasTxt.font          = 20;

        canvasTxt.drawText(
            ctx, 
            $(text2).val(),
            20, 
            20, 
            0, 
            0
        );
        ctx.restore() // <----------- ADD
    
;

function hexToRgb(hex) 
    var result = /^#?([a-f\d]2)([a-f\d]2)([a-f\d]2)$/i.exec(hex);
    return result ? 
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
     : null;
;

function createGradient(hex, alpha) 

    const r = hexToRgb(hex).r.toString();
    const g = hexToRgb(hex).g.toString();
    const b = hexToRgb(hex).b.toString();

    var gradient =  ctx.createLinearGradient(800, 0, 0, 0);
    gradient.addColorStop(0, `rgba($r, $g, $b, $alpha)`);

    ctx.save() // <----------- ADD
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.restore() // <----------- ADD
;


function draw() 
    canvas.width        = this.naturalWidth;
    canvas.height       = this.naturalHeight;
    const nw            = this.naturalWidth;
    const nh            = this.naturalHeight;

    ctx.drawImage(this, 0, 0, nw, nh);
;

function failed() 
    console.error("The provided file couldn't be loaded as an Image media");
;


$(btnDownload).on('click', function(e) 
    const a = document.createElement('a');
    document.body.appendChild(a);
    a.href = canvas.toDataURL();
    a.download = "canvas-image.png";
    a.click();
    document.body.removeChild(a);
);    
#canvas
    background-color: transparent; 
    width: 30%; 
    height: auto;
    border: 1px solid #777;
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://unpkg.com/canvas-txt@3.0.0/build/index.js"></script>

<canvas id="canvas"  ></canvas>

<div>
    <input type="file" class="file-upload" />
    <button class="btnDownload">Download</button>
</div>


<div>
  <form id="text1-form">
    <input type="text" id="text1" placeholder="text 1"/> 
  </form>
</div>

<div>
  <form id="text2-form">
    <input type="text" id="text2" placeholder="text 2"/> 
  </form>
</div>

<div>
  <h2>Image Gradient and Opacity</h2>
  <form id="image-form">
    <input type="color" id="gradientcolor" value="#000000" />
    <input type="range" id="gradientopacity" min="0" max="1" value="0" step="0.05" />
  </form>
</div>


<div>
  <h2>Text2 Shadow Offset X</h2>
  <input type="color" id="text2shadowcolor" value="#000000" />
  <input type="range" id="text2shadowoffy" min="0" max="40" value="0" />
</div>

【讨论】:

嘿伙计,很抱歉打扰您。但我需要基于此画布的其他帮助。现在,如果我上传一张图片并在上面写一些东西,它就可以正常工作。但是如果我想改变图像,那么之前写的文字就会消失。如何将文本保持在顶部并能够根据需要多次更改图像? ***.com/questions/66611694/…

以上是关于画布线性渐变采用另一个输入类型颜色字段的值的主要内容,如果未能解决你的问题,请参考以下文章

在给定任意颜色范围的情况下均匀分布画布渐变颜色

我无法获得连续的画布矩形在js中具有重复的线性渐变。谁知道怎么样?

SVG渐变

CSS中的渐变——线性渐变

线性渐变和重复渐变

Canvas 线性渐变原理详解