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

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

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

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

  1. 上传随机图片。
  2. Text 2 Shadow Offset X 拾色器中选择一种颜色。
  3. 增加Text 2 Shadow Offset X 拾色器旁边的滑块。
  4. 然后增加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" width="800" height="500"></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 函数。

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

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

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

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

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

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

提前致谢

【问题讨论】:

    标签: javascript html jquery canvas html5-canvas


    【解决方案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" width="800" height="500"></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" width="800" height="500"></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>

    【讨论】:

    • 嘿伙计,很抱歉打扰您。但我需要基于此画布的其他帮助。现在,如果我上传一张图片并在上面写一些东西,它就可以正常工作。但是如果我想改变图像,那么之前写的文字就会消失。如何将文本保持在顶部并能够根据需要多次更改图像?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 2019-06-30
    • 2016-10-10
    • 2016-05-31
    • 2021-12-30
    • 1970-01-01
    相关资源
    最近更新 更多