z-index в canvas

109
01 марта 2021, 14:20

const canvas = document.getElementById('canvas'); 
var ctx = canvas.getContext('2d'); 
ctx.lineJoin = 'round'; 
ctx.fillStyle = 'black'; 
 
var previous, 
  inputSize = document.querySelector("[name='size']"), 
  rect; 
 
function Paint(elem) { 
  var x, 
    y, 
    width, 
    height; 
 
  this.image = { 
    x: 0, 
    y: 0, 
    color: 'black', 
    tool: null, 
 
    coordsOfCanvas: () => { 
      return canvas.getBoundingClientRect(); 
    }, 
  }; 
 
  this.mouseDown = false; 
 
  this.pencil = () => { 
    this.image.tool = 'pencil'; 
    ctx.lineWidth = inputSize.value = '5'; 
    ctx.lineCap = 'round'; 
    ctx.strokeStyle = 'black'; 
 
    inputSize.parentNode.style.display = 'table-cell'; 
  }; 
 
  this.brush = () => { 
    this.image.tool = 'brush'; 
    ctx.lineWidth = inputSize.value = '25'; 
    ctx.lineCap = 'butt'; 
    ctx.strokeStyle = 'black'; 
 
    inputSize.parentNode.style.display = 'table-cell'; 
  }; 
 
  this.fill = () => { 
    this.image.tool = 'fill'; 
    ctx.fillStyle = 'black' 
 
    inputSize.parentNode.style.display = 'none'; 
  }; 
 
  this.clear = () => { 
    this.image.tool = 'clear'; 
    ctx.strokeStyle = 'white'; 
    ctx.lineWidth = inputSize.value = '20'; 
    ctx.lineCap = 'butt'; 
 
    inputSize.parentNode.style.display = 'table-cell'; 
  }; 
 
  this.rectangle = () => { 
    this.image.tool = 'rectangle'; 
    inputSize.parentNode.style.display = 'none'; 
  }; 
 
  //Координаты мыши относительно canvas 
  this.getCoords = () => { 
    this.image.x = event.clientX - this.image.coordsOfCanvas().left; 
    this.image.y = event.clientY - this.image.coordsOfCanvas().top; 
  } 
 
  elem.addEventListener('mousedown', () => { 
    this.mouseDown = true; 
    this.getCoords(); 
 
    if (this.image.tool == 'fill') { 
      ctx.fillRect(0, 0, canvas.width, canvas.height); 
    } else if ((this.image.tool == 'pencil' || 
        this.image.tool == 'brush' || 
        this.image.tool == 'clear') && this.mouseDown) { 
      ctx.beginPath(); 
      ctx.moveTo(this.image.x, this.image.y); 
    } else if (this.image.tool == 'rectangle' && this.mouseDown) { 
      //Фиксируем начальные координаты 
      x = this.image.x; 
      y = this.image.y; 
    } 
 
    elem.addEventListener('mousemove', () => { 
      this.getCoords(); 
 
      if ((this.image.tool == 'pencil' || 
          this.image.tool == 'brush' || 
          this.image.tool == 'clear') && this.mouseDown) { 
        ctx.lineWidth = inputSize.value; 
        ctx.lineTo(this.image.x, this.image.y); 
        ctx.stroke(); 
      } 
 
      if (this.image.tool == 'rectangle' && this.mouseDown) { 
        ctx.clearRect(x, y, width - x, height - y) 
        ctx.fillRect(x, y, this.image.x - x, this.image.y - y) 
        ctx.fillStyle = 'black' 
 
        //Сохраняем предыдущие координаты мыши 
        width = this.image.x; 
        height = this.image.y; 
      } 
    }); 
 
  }); 
 
  elem.addEventListener('mouseup', () => { 
    this.mouseDown = false; 
 
    if (this.image.tool == 'rectangle') { 
      x = y = height = width = null; 
    } 
  }); 
 
 
  document.body.addEventListener('click', () => { 
    let target = event.target; 
    let action = target.getAttribute('data-type'); 
 
    if (action) { 
 
      if (previous) { 
        previous.classList.remove('active'); 
      } 
 
      target.classList.add('active'); 
      previous = target; 
      this[action](); 
    }; 
 
  }); 
}; 
 
new Paint(canvas);
body { 
  position: relative; 
  min-height: 100vh; 
  background: gray; 
  margin: 0; 
} 
 
.draw { 
  position: absolute; 
  top: 0; 
  right: 10%; 
  bottom: 0; 
  left: 10%; 
  display: flex; 
  justify-content: center; 
} 
 
.options { 
  position: absolute; 
  top: 0; 
  right: 0; 
  left: 48px; 
  height: 48px; 
  background: #575a5e; 
  z-index: 1; 
} 
 
.options label { 
  display: block; 
} 
 
.options ul { 
  list-style: none; 
  margin: 0; 
  margin-left: -40px; 
} 
 
.options ul li { 
  display: none; 
  height: 48px; 
  color: white; 
  font-size: 14px; 
  padding: 0 5px; 
  vertical-align: middle; 
} 
 
.tools { 
  position: absolute; 
  top: 0%; 
  left: 0%; 
  bottom: 0%; 
  width: 48px; 
  background: #575a5e; 
  z-index: 2; 
} 
 
.tools>div { 
  width: 48px; 
  height: 48px; 
} 
 
.tools div:hover { 
  cursor: pointer; 
} 
 
.pencil { 
  background: url('icons.png'); 
  background-position-x: 0; 
} 
 
.brush { 
  background: url('icons.png'); 
  background-position-x: -48px; 
} 
 
.clear { 
  background: url('icons.png'); 
  background-position-x: -96px; 
} 
 
.fill { 
  background: url('icons.png'); 
  background-position-x: -144px; 
} 
 
.rectangle { 
  background: url('icons.png'); 
  background-position-x: -48px; 
  background-position-y: -48px; 
} 
 
.active { 
  background-color: #cdd0d6; 
} 
 
canvas { 
  border: 1px solid black; 
  background: white; 
  margin-top: 150px; 
  margin-bottom: 150px; 
}
<div class='tools'> 
  <div class='pencil' data-type='pencil'></div> 
  <div class='brush' data-type='brush'></div> 
  <div class='fill' data-type='fill'></div> 
  <div class='clear' data-type='clear'></div> 
  <div class='rectangle' data-type='rectangle' style='background: green'></div> 
</div> 
<div class='options'> 
  <ul> 
    <li class='size'> 
      <label for="size">Size(1-50): </label> 
      <input type="number" id="size" name='size' min="1" max="50" value=''> 
    </li> 
  </ul> 
</div> 
<div class='draw'> 
  <canvas width='800px' height='500px' id='canvas'></canvas> 
</div>

У меня 2 вопроса:

1) Возможно ли как-нибудь образом реализовать наложение рисованных фигур одну на вторую? На данный момент, если наложить 1 прямоугольник на 2, не отпуская лкм, а затем 1-ому уменьшить размер - сотрёт часть уже нарисованного.

2) Если нарисовать несколько прямоугольников, некоторые их части пропадают с canvas чудесным образом :) Из-за чего?

P.S. Выбрать рисование прямоугольником - нажать на зелёный div

Answer 1

Вот примерно то, о чем я писал в комментарии

let a, c = canvas.getContext('2d'), s = canvas.width, r = []; 
 
canvas.addEventListener('mousedown', e =>  
    r.push(a = [e.x,e.y,0,0,`hsl(${Math.random()*360},55%,55%)`])); 
 
canvas.addEventListener('mouseup', e => a = null); 
 
canvas.addEventListener('mousemove', e => { 
  if (!a) return; 
  a[2] = e.x - a[0], a[3] = e.y - a[1]; 
  c.clearRect(0, 0, s, s); 
  let t = new Date; 
  r.forEach(r => (c.fillStyle = r[4]) && c.fillRect(...r)); 
  c.fillText(r.length + ' - ' + (new Date - t) + 'ms', 3, 10); 
})
body{margin:0}canvas{background-color:rgba(0,0,0,0.05)}
<canvas id="canvas" width="635" height="175"></canvas>

READ ALSO
Чем паттерн &ldquo;revealing module&rdquo; лучше паттерна &ldquo;module&rdquo;?

Чем паттерн “revealing module” лучше паттерна “module”?

В чем преимущество такой записи:

74
На декстопе не работает IntersectionObserver

На декстопе не работает IntersectionObserver

Проблема в том, что IntersectionObserver API не работает в декстопной версии на VueКак только включаешь режим Mobile, всё грузится

96
Получение значения нажатого элемента

Получение значения нажатого элемента

Можно ли как-нибудь получить значение элемента на который я нажал? (при нажатии на блок со значением 1 выводило 1, 2-2, 3-3 и тд)

97