0

So I'm trying to recreate a rainbow scratch paper effect. It works in two layers: a sheet of rainbow paper, with a black coating on top. I think the easiest thing to do would be to create two canvases – one black, layered on top of one rainbow. Is there a simpler approach? I am having a couple issues:

  • my black canvas can isn't showing up!
  • for my rainbow canvas can2, I made found a gradient maker here, but am unsure how to make it fill the entire page.

Here's the code:

    <style>
      body{ margin: 0; } canvas {position: absolute;}
    </style>

<body>

  <canvas id ="can"></canvas>
  <canvas id ="can2"></canvas>

  <script>

    var can = document.getElementsByTagName('canvas')[0];
        can.width = window.innerWidth;
        can.height = window.innerHeight;
    var ctx = can.getContext('2d');
    var fillBlack = can.getElementById("can");
    fillBlack.style.color = "black";

    var can2 = document.getElementsByTagName('canvas')[1];
        can2.width = window.innerWidth;
        can2.height = window.innerHeight;
    var ctx2 = can2.getContext('2d');

        colorScratch(.1, .2, .3, 0, 0, 0, 128, 127, 50);

    var canBox = can.getBoundingClientRect();

    ctx.clearRect(0, 0, can.width, can.height);

    function colorScratch(freq1, freq2, freq3, phase1, phase2, phase3, center, width, len) {
      for (var x = 0; x < len; x++){
        for (var y = 0; y < width; y++){
         var red = Math.sin(freq1 * x + phase1) * y + center);
         var grn = Math.sin(freq2 * x + phase2) * y + center);
         var blu = Math.sin(freq3 * x + phase3) * y + center);
         ctx2.fill( '<div color="' + RGB2Color(red,grn,blu) + '">&#9608;</div>');
        }
      }
    }

    var stamp_shape = function(color, pos, size) {
      ctx.beginPath();
      ctx.arc(pos[0], pos[1], size, 0, 2*Math.PI);
      ctx.fillStyle = color;
    };

    var stamp = function(x, y, size) {
      var center = size / 2;
      ctx.save();
      ctx.globalAlpha = 0.6;
      var grd = ctx.createRadialGradient(x, y, size, x, y, 0);
      grd.addColorStop(1, 'rgba(255, 200, 200, 0.2)');
      grd.addColorStop(0, 'rgba(255, 255, 255, 0)');
      stamp_shape(grd, [x-center, y-center], size);
      ctx.restore();
    };

    function getCoords(event) {
      return {
        x: event.pageX - canBox.left,
        y: event.pageY - canBox.top
      };
    }

    var drawing = false;

    can2.onmousedown = function() {
      drawing = true;
    }

    can2.onmouseup = function() {
      drawing = false;
    }

    can2.onmousemove = function(e) {
      var mousePos = getCoords(e);
      if (!drawing) {
        drawCursor(mousePos.x, mousePos.y, 50);
        console.log(mousePos);
        return;
      }
      clearCursor();
      stamp(mousePos.x, mousePos.y, 50);
    }

    function drawCursor(x, y, size) {
      ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
      ctx2.strokeStyle = '#F66';
      ctx2.strokeRect(x - size / 2, y - size / 2, size, size);
    }

    function clearCursor() {
      ctx2.clearRect(0, 0, ctx2.canvas.width, ctx2.canvas.height);
    }

  </script>
</body>

Thank you so much to anyone who can take a look!! xx

jess_JS
  • 15
  • 7
  • First off, spaces are not allowed in ids, see http://stackoverflow.com/questions/70579/what-are-valid-values-for-the-id-attribute-in-html Changing that would allow you to use getElementById instead – phenxd Mar 01 '16 at 18:33
  • oh wow thank you. I was initially trying to use multiple ids, which I think only need to be separated by spaces. I see what you mean though. I'll change the code now – jess_JS Mar 01 '16 at 18:37
  • Also try Google Chrome's console. You have parsing errors around your javascript line 22-24 – phenxd Mar 01 '16 at 18:39
  • you can't have multiple ids for 1 element. It wouldn't make sense since every id must be unique anyway. Maybe you were thinking about classes. – phenxd Mar 01 '16 at 18:40
  • @phenxd I was definitely thinking about classes. Thank you again. – jess_JS Mar 01 '16 at 18:47

2 Answers2

0

I tried to run it on JSFiddle but RGB2Color() was undefined.

ctx2.fill( '<div color="' + RGB2Color(red,grn,blu) + '">&#9608;</div>');

I kinda get what you,re trying to do here, but you can't fill a canvas with a div. You'll have to find some other way.

Anyway, the way I see it, the rainbow doesn't really need to be a canvas if it is a fixed image. It could be either a drawn in div or an image. Tough if the rainbow changes over time it probably needs to be a canvas.

You could then layer the black canvas over it for your desired effect.

phenxd
  • 689
  • 4
  • 17
0

Your idea of placing a solid color opaque canvas directly over a gradient image (or canvas) is a good idea.

Then as the user drags, you can use context.globalCompositeOperation='destination-out' to "erase" the solid color and reveal the gradient underneath

Here's example code and a Demo:

var canvas=document.getElementById("canvasTop");
var ctx=canvas.getContext("2d");
var canvas2=document.getElementById("canvasBottom");
var ctx2=canvas2.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var isDown=false;
var startX,startY;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/rainbowGradient.jpg";
function start(){
  cw=canvas.width=canvas2.width=img.width;
  ch=canvas.height=canvas2.height=img.height;
  ctx2.drawImage(img,0,0);
  ctx.lineWidth=10;
  ctx.lineCap = "round";
  ctx.lineJoin = "round";
  ctx.fillStyle="skyblue";
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.globalCompositeOperation="destination-out";

  $("#canvasTop").mousedown(function(e){handleMouseDown(e);});
  $("#canvasTop").mousemove(function(e){handleMouseMove(e);});
  $("#canvasTop").mouseup(function(e){handleMouseUpOut(e);});
  $("#canvasTop").mouseout(function(e){handleMouseUpOut(e);});
}

function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);

  // Put your mousedown stuff here
  isDown=true;
}

function handleMouseUpOut(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // Put your mouseup stuff here
  isDown=false;
}

function handleMouseMove(e){
  if(!isDown){return;}
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // Put your mousemove stuff here
  ctx.beginPath();
  ctx.moveTo(startX,startY);
  ctx.lineTo(mouseX,mouseY);
  ctx.stroke();
  startX=mouseX;
  startY=mouseY;
}
body{ background-color: ivory; }
#wrapper{
  position:relative;
}
#canvasTop,#canvasBottom{
  position:absolute; top:0px; left:0px;
  border:1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag to reveal gradient.</h4>
<div id="wrapper">
  <canvas id="canvasBottom" width=300 height=300></canvas>
  <canvas id="canvasTop" width=300 height=300></canvas>
</div>
markE
  • 102,905
  • 11
  • 164
  • 176