1

Below is provided a script for changing image on click, changes will be affected in canvas.

The script is working properly in Firefox and Chrome but not in Safari

What could be the reason?

<script>
  $(function() {

    var house = document.getElementById("house");
    var ctxHouse = house.getContext("2d");
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    var canvas2 = document.getElementById("canvas2");
    var ctxHand = canvas2.getContext("2d");
    var FabricLink;
    var imageObj = new Image();
    var red = new Image();
    red.onload = function() {
      canvas.width = red.width;
      canvas.height = red.height;

      var houseImage = new Image();
      houseImage.onload = function() {
        house.width = houseImage.width;
        house.height = houseImage.height;
        ctxHouse.drawImage(houseImage, 0, 0);
      }
      houseImage.src = "images/img.jpg";

      ctx.drawImage(red, 0, 0);


      imageObj.onload = function() {
        var pattern = ctx.createPattern(imageObj, 'repeat');
        ctx.globalCompositeOperation = 'source-in';
        ctx.rect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = pattern;
        ctx.fill();
      };
      $(imageObj).attr('src', 'images/' + FabricLink + '.png');
    }
    red.src = "images/img.png";
    var imageObj2 = new Image();
    var blue = new Image();
    blue.onload = function() {
      canvas2.width = blue.width;
      canvas2.height = blue.height;

      var houseImage = new Image();
      houseImage.onload = function() {
        house.width = houseImage.width;
        house.height = houseImage.height;
        ctxHouse.drawImage(houseImage, 0, 0);
      }
      houseImage.src = "images/img.jpg";

      ctxHand.drawImage(blue, 0, 0);


      imageObj2.onload = function() {
        var pattern = ctx.createPattern(imageObj2, 'repeat');
        ctxHand.globalCompositeOperation = 'source-in';
        ctxHand.rect(0, 0, canvas2.width, canvas2.height);
        ctxHand.fillStyle = pattern;
        ctxHand.fill();
      };
      $(imageObj2).attr('src', 'images/' + FabricLink + '.png');
    }
    blue.src = "images/img.png";




    $('#style li').click(
      function() {
        //we get our current filename and use it for the src
        var linkIndex = $(this).attr("data-filename");
        $(red).attr('src', 'images/' + linkIndex + '.png');
      }
    );

    $('#Sleeves li').click(
      function() {
        $("#canvas2").addClass("show-z");
        //we get our current filename and use it for the src
        var SleevesLink = $(this).attr("data-filename");
        $(blue).attr('src', 'images/' + SleevesLink + '.png');
      }
    );

    $('#Fabric li').click(
      function() {
        //we get our current filename and use it for the src
        FabricLink = $(this).attr("data-filename");
        $(imageObj2).attr('src', 'images/' + FabricLink + '.png');
        $(imageObj).attr('src', 'images/' + FabricLink + '.png');


      }
    );

    $("#Fabric li:first-child").click();




  }); // end $(function(){});
</script>

While debugging, we could see that imageObj.onload is not getting fired in the first click , only on refresh the change occurs in Safari.

jsfiddle.net/qvfx3kz7 (In the fiddle images are not loading, but the complete code is added)

Kaiido
  • 123,334
  • 13
  • 219
  • 285
Aashiq Rathnadas
  • 515
  • 1
  • 5
  • 24
  • Just asking out of curiosity, did you try doing your stuff in `$(window).load(function() {...})` instead of `imgObject.onload = function() {...}`? What were the results? – Fr0zenFyr May 04 '18 at 10:57
  • @Fr0zenFyr tried that, then the issue has came on chrome too, no change in safari – Aashiq Rathnadas May 04 '18 at 11:07
  • can you make a demo fiddle or jsbin that I can check? – Fr0zenFyr May 04 '18 at 11:08
  • okay, will add now – Aashiq Rathnadas May 04 '18 at 11:14
  • https://jsfiddle.net/qvfx3kz7/ @Fr0zenFyr , the images here are not loading , but added all the code – Aashiq Rathnadas May 04 '18 at 11:28
  • Why are you nesting all your image onload handlers in other image onload handlers like that? Really hard to read, and not that much surprising you face issues. So I'll start with the usual question when facing an Safari only issue: *Which version of Safari are you using?* And then jump on *Since you seem to be drawing fabric's pattern, I guess your images are quite small, can't you create a single spritesheet containing all these small images, and then play with the arguments of drawImage in order to create your pattern synchronously from an offscreen canvas?* – Kaiido May 04 '18 at 14:26
  • @Kaiido Thank you so much for your edit and comment, our requirement is some how similar to below link, we have to use multiple canvas and images onClick,so added that https://stackoverflow.com/questions/15526943/in-html5-canvas-how-to-mask-an-image-with-a-background-of-my-choice – Aashiq Rathnadas May 05 '18 at 04:31

1 Answers1

1

I am not sure what is exactly the problem with Safari, but what I can see is that you are over-complicating everything by trying to load your assets only when requested, which leads to this callback nightmare you have set up.

Instead, prepare your assets so that you can load it only once. Since you are using patterns, I will assume that each pattern is actually a small image.
So instead of loading every little pattern images, load a single sprite-sheet, which will contain all your patterns.
From there, you'll be able to generate dynamically your CanvasPattern synchronously, without having to care for any loading callback. In order to do this, you will have to use a second, off-screen, canvas, that you will use as he source of createPattern() method:

// really dummy implementation...
function AssetsLoader() {}
AssetsLoader.prototype = Object.create({
  addImage: function(objs, cb) {
    if (!objs || typeof objs !== 'object') return;
    if (!Array.isArray(objs)) objs = [];
    var toLoad = objs.length,
      imgs = objs.map(loadImage, this);

    function loadImage(obj) {
      if (!obj.src) return;
      this.toLoad++;
      var img = new Image();
      img.src = obj.src;
      img.onload = onimgload;
      img.onerror = onimgerror;
      return obj.img = img;
    }

    function onimgload(evt) {
      if (--toLoad <= 0 && typeof cb === 'function') cb(imgs);
    }

    function onimgerror(evt) {
      console.warn('failed to load image at ', evt.target.src);
    }
  }
});
// Some info about our assets, there are an infinity of ways to deal with it,
// You would have to determine yourself what's best for your own case
var dests = {
  sofa: {
    name: 'sofa',
    src: 'https://i.stack.imgur.com/ryO42.png'
  },
  shirt: {
    name: 'shirt',
    src: 'https://i.stack.imgur.com/cPNbe.png'
  }
};
var patterns = {
  name: 'patterns',
  src: 'https://i.stack.imgur.com/TdIAJ.png',
  positions: {
    orange: {
      x: 0,
      y: 0,
      width: 173,
      height: 173,
      out_width: 75,
      out_height: 75
    },
    violet: {
      x: 173,
      y: 0,
      width: 173,
      height: 173,
      out_width: 35,
      out_height: 35
    },
    psyche: {
      x: 0,
      y: 173,
      width: 173,
      height: 173,
      out_width: 25,
      out_height: 25
    },
    pink: {
      x: 173,
      y: 173,
      width: 173,
      height: 173,
      out_width: 125,
      out_height: 125
    }
  }
}

var assets = new AssetsLoader();
// first load all our images, and only then, start the whole thing
assets.addImage([dests.shirt, dests.sofa, patterns], init);


function init() {
  // populate our selects
  for (var key in dests) {
    dest_select.appendChild(new Option(key, key));
  }
  for (var key in patterns.positions) {
    pat_select.appendChild(new Option(key, key));
  }
  dest_select.onchange = pat_select.onchange = draw;

  var ctx = canvas.getContext('2d');
  var offscreenCtx = document.createElement('canvas').getContext('2d');

  draw();

  function draw() {
    var dest = dests[dest_select.value].img;
    ctx.canvas.width = dest.width;
    ctx.canvas.height = dest.height;

    ctx.globalCompositeOperation = 'source-over';
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(dest, 0, 0, canvas.width, canvas.height);
    ctx.globalCompositeOperation = 'source-in';

    // here we generate the CanvasPattern
    ctx.fillStyle = getPattern(pat_select.value);
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.globalCompositeOperation = 'luminosity';
    ctx.drawImage(dest, 0, 0, canvas.width, canvas.height);
  }
  // now that we have our patterns loaded in a single sprite-sheet,
  // we can generate the CanvasPatterns synchronously
  function getPattern(key) {
    var pos = patterns.positions[key]; // get our pattern's position in our dictionary
    // resize the offscreen canvas so it matches our pattern
    offscreenCtx.canvas.width = pos.out_width;
    offscreenCtx.canvas.height = pos.out_height;
    offscreenCtx.drawImage(patterns.img, pos.x, pos.y, pos.width, pos.height, 0, 0, pos.out_width, pos.out_height);
    return offscreenCtx.createPattern(offscreenCtx.canvas, 'repeat');
  }
}
<select id="dest_select"></select>
<select id="pat_select"></select>
<br><br><br>
<canvas id="canvas"></canvas>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • this is what we need, but in our requirement say for t-shirt we need hand(sleeves) need to be another image, that has to be changed, and it should work in safari(with pretty old versions like 8+) – Aashiq Rathnadas May 08 '18 at 04:19
  • @AashiqRathnadas well the example does that too, from a tshirt to a sofa and the same basic advice applies: load once your assets and store it so you can use them synchronously. – Kaiido May 08 '18 at 04:50
  • thanks for your time, https://www.houseofblouse.com/design/v-neck-1 this site is working properly in our safari, and that type of changing is what we need. – Aashiq Rathnadas May 08 '18 at 05:25