Search code examples
javascriptcanvasfabricjs

Can I apply two shaded color on single image with image filter in fabricjs?


I have two shaded colors like this:

with color red and blue

I want to apply gradient colors on single image with fabric js image filter.

Basically I added two palettes for colors. What I need is when I change color from palettes that both colors apply in a single image like the above image. Is that possible?

I am designing a T-shirt like below:

The t-shirt

I want to apply those two colors on a big grey image of the T-shirt. Currently different color images are working as separate image and separate object in fabric js and I want to apply those colors on single grey image part.

Is there any way to make this work?


Solution

  • Since FabricJs doesn't give natively the possibility of apply a gradient to an Image, you can achieve your task with the ImageFilter included in the following code snippet.

    (function(global) {
    
          'use strict';
    
          var fabric = global.fabric || (global.fabric = {}),
            extend = fabric.util.object.extend;
    
          fabric.Image.filters.GradientEffect = fabric.util.createClass(fabric.Image.filters.BaseFilter, {
    
            type: 'GradientEffect',
    
            initialize: function (options) {
              options = options || {};
              this.gradient = options.gradient || {};
              this.img = options.img;
            },
    
            applyTo: function(canvasEl) {
              var gr = this.gradient;
              var w = this.img._element.naturalWidth;
              var h = this.img._element.naturalHeight;
              var hc = document.createElement('canvas');
    
              hc.setAttribute('width', w);
              hc.setAttribute('height', h);
    
              var fhc = new fabric.Canvas(hc);
              var rect = new fabric.Rect({
                fill: 'transparent',
                width: w,
                height: h
              });
    
              rect.setGradient('fill', gr);
    
              fhc.add(rect);
              fhc.renderAll();
    
              var ifhcContext = fhc.getContext('2d');
              var fhcImageData = ifhcContext.getImageData(0, 0, fhc.width, fhc.height);
              var fhcData = fhcImageData.data;
    
              var context = canvasEl.getContext('2d'),
                imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
                data = imageData.data;
    
              for (var i = 0, n = data.length; i < n; i += 4) {
                if(data[i] != 0 && data[i+1] != 0 && data[i+2] != 0) {
                  data[i] += fhcData[i];
                  data[i + 1] += fhcData[i + 1];
                  data[i + 2] += fhcData[i + 2];
    
                }
              }
    
              context.putImageData(imageData, 0, 0);
            },
    
            toObject: function() {
              return extend(this.callSuper('toObject'), {
                gradient: this.gradient
    
              });
            }
          });
    
          fabric.Image.filters.GradientEffect.fromObject = function(object) {
            return new fabric.Image.filters.GradientEffect(object);
          };
    
        })(typeof exports !== 'undefined' ? exports : this);
    
    
    var canvas = new fabric.Canvas('c');
    
    fabric.Image.fromURL('http://i.imgur.com/bkMw5mx.png', function(img) {
      var gr = {
        x1: 0,
        y1: 0,
        x2: 0,
        y2: img.height,
        colorStops: {
          0: "red",
          1: "blue",
        }
      };
      img.filters.push(new fabric.Image.filters.GradientEffect({
        gradient: gr,
        img: img,
        
      }));
      img.applyFilters(canvas.renderAll.bind(canvas));
      canvas.add(img.set({
        width: 150,
        height: 150,
    
      }));
    }, {
      crossOrigin: ''
    });
    .floatLeft {
      display: inline-block;
      border: 2px solid #ccc;
    }
    .placeholder {
      margin-left: 10px;
    }
    <script src="//cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
    <img class='image floatLeft' src='http://i.imgur.com/bkMw5mx.png' width='200' height='200'>
    <div class='placeholder floatLeft'>
      <canvas id='c' width='300' height='300'></canvas>
      <div>


    The work is done by applyTo function, I create a new Canvas on the fly and I put a rectangle filled with the argument gradient on it, then, pixel by pixel, I apply the relative color picked from the gradient.:

    applyTo: function(canvasEl) {
              var gr = this.gradient;
              var w = this.img._element.naturalWidth;
              var h = this.img._element.naturalHeight;
              var hc = document.createElement('canvas');
    
              hc.setAttribute('width', w);
              hc.setAttribute('height', h);
    
              var fhc = new fabric.Canvas(hc);
              var rect = new fabric.Rect({
                fill: 'transparent',
                width: w,
                height: h
              });
    
              rect.setGradient('fill', gr);
    
              fhc.add(rect);
              fhc.renderAll();
    
              var ifhcContext = fhc.getContext('2d');
              var fhcImageData = ifhcContext.getImageData(0, 0, fhc.width, fhc.height);
              var fhcData = fhcImageData.data;
    
              var context = canvasEl.getContext('2d'),
                imageData = context.getImageData(0, 0, canvasEl.width, canvasEl.height),
                data = imageData.data;
    
              for (var i = 0, n = data.length; i < n; i += 4) {
                if(data[i] != 0 && data[i+1] != 0 && data[i+2] != 0) {
                  data[i] += fhcData[i];
                  data[i + 1] += fhcData[i + 1];
                  data[i + 2] += fhcData[i + 2];
    
                }
              }
    
              context.putImageData(imageData, 0, 0);
            }