Как повернуть форму холста html, используя только преобразование? - PullRequest
0 голосов
/ 15 февраля 2020

Мне интересно, как вы можете повернуть изображение, используя только функцию преобразования. Насколько я понимаю, это невозможно, поскольку единственное, что вы можете сделать с преобразованием, это:

  • Горизонтальное масштабирование
  • Горизонтальный наклон
  • Вертикальный наклон
  • Вертикальное масштабирование
  • Горизонтальное перемещение
  • Вертикальное перемещение

Источник: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Transformations

И I не вижу, как любой из них сможет вращать форму, это даже возможно. Я предполагаю, что это должно быть возможно, поскольку вращение на самом деле является преобразованием типа.

Ответы [ 4 ]

1 голос
/ 16 февраля 2020

Основы 2D-преобразования

6 значений в виде 3 векторов

Преобразование представляет собой набор из 6 чисел. 6 чисел в виде 3 пар представляют направление и масштаб оси x, направление и масштаб оси y, а также положение начала координат.

Преобразование по умолчанию AKA Identity matrix

преобразование по умолчанию (называемое единичной матрицей) имеет значения ctx.setTransform(1, 0, 0, 1, 0, 0), означающие, что

  • ось x представляет собой 1 преобразованный пиксель на CSS пиксель в направлении {x: 1, y: 0} слева направо
  • ось y представляет собой 1 преобразованный пиксель на CSS пиксель в направлении {x: 0, y: 1} сверху вниз
  • начало координат в расположении пикселей {x: 0, y: 0} в верхнем левом углу

Масштабирование

Если мы масштабируем преобразование, мы увеличим длину первых двух векторов. Для масштабирования в 2 преобразования: ctx.setTransform(2, 0, 0, 2, 0, 0);

  • ось x равна 1 преобразованному пикселю на каждые 2 CSS пикселя в направлении x {x: 2, y: 0} слева направо
  • ось y - это 1 преобразованный пиксель на каждые 2 CSS пикселей в направлении y {x: 0, y: 2} сверху вниз
  • начало координат по-прежнему слева вверху {x: 0, y: 0}

Поворот и перевод

Если мы хотим повернуть на 90 градусов квадратное изображение 256 на 256, тогда преобразование будет ctx.setTransform(0, 1, -1, 0, 256, 0)

  • ось x равна 1 преобразованному пикселю на CSS пиксель вниз в направлении y {x: 0, y: 1}
  • ось y - 1 преобразованный пиксель на CSS пикселей в отрицательном направлении x {x: -1, y: 0} справа налево
  • начала координат ( где изображение 0, 0 будет на холсте) равно {x: 256, y: 0}

Таким образом, если мы запустим

ctx.setTransform(0, 1, -1, 0, 256, 0);
ctx.drawImage(myImage, 0, 0, 256, 256); // will draw image rotated 90deg CW

Мы получим повернутое изображение.

Вектор

Вектор - это два значения, которые имеют значения ax и y. Вектор определяет направление и длину.

Создание повернутого единичного вектора

Для преобразования направления в вектор мы используем sin и cos

const myDirection = angle;
const myDirectionAsRadians = angle * (Math.PI / 180);  // convert angle to radians

const x = Math.cos(myDirectionAsRadians)
const y = Math.sin(myDirectionAsRadians)

Если мы установим * От 1066 * до 90 (град), затем x = 0 и y = 1, указывающих вниз по холсту

Использование sin и cos создает вектор в любом направлении. Он имеет особое свойство в том, что его длина всегда равна 1. Мы называем такой вектор единичным вектором. Иногда вы можете увидеть нормализованный вектор. Это преобразует вектор любой длины в единичный вектор. Это делается путем деления вектора x и y на его длину.

 function normalize(vector) {
     const length = Math.hypot(vector.x, vector.y);
     vector.x /= length;
     vector.y /= length;
 }

ПРИМЕЧАНИЕ вектор с нулевой длиной, например x: 0, y:0, не может быть нормализован. Не потому, что у него нет длины (длина равна 0), а потому, что у него нет направления.

Масштабировать повернутый вектор

Мы можем определить угол и масштаб

const myDirection = -90;
const myDirectionAsRadians = -90 * (Math.PI / 180);  // -90 as radians
const myScale = 2;

const x = Math.cos(myDirectionAsRadians) * myScale
const y = Math.sin(myDirectionAsRadians) * myScale

Теперь для -90 градусов вектор x = 0 и y = -2 направлен вверх и имеет длину 2 CSS пикселей.

Вектор с быстрым поворотом 90 градусов CW

Для равномерного масштабирования и поворота (изображение всегда квадратное) все, что нам нужно, это один вектор. Например из вышесказанного. x = 0 и y = -2 (вверх) можно повернуть на 90 CW, поменяв местами два компонента и отрицая новый x. например, xx = -y и y = x для получения xx = 2 и y = 0 2 CSS пикселей слева направо. Таким образом, мы имеем направление и масштаб осей x и y. С осью y всегда 90 CW от x.

Использование преобразования для рисования

Создание повернутого и масштабированного преобразования

Чтобы создать преобразование, которое поворачивает любой угол и масштабирует любое количество

 function scaleAndRotate(scale, rotate) { // rotate is in radians
     // get direction and length of x axis
     const xAX = Math.cos(rotate) * scale;
     const xAY = Math.sin(rotate) * scale;
     // get direction and length of y axis that is 90 deg CW of x axis and same length
     const [yAX, yAY] = [-xAY, xAX];  // swap and negate new x

     // set the transform
     ctx.setTransform(xAX, xAY, yAX, yAY, 0, 0);

 }

Рисование изображения

Позволяет создать функцию, которая будет рисовать изображение в любом месте холста, который вращается и масштабируется равномерно. Мы будем использовать центр изображения в качестве опорной точки

 function drawImageScaleRotate(img, x, y, scale, rotate) {
      // define the direction and scale of x axis
      const xAX = Math.cos(rotate) * scale;
      const xAY = Math.sin(rotate) * scale;

      // create the transform with yaxis at 90 CW of x axis and origin at x, y
      ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);

      // Draw the image so that its center is at the new origin x, y
      ctx.drawImage(img, -img.width / 2, -img.height / 2);

   }

Существует гораздо больше

1111 * Когда мы устанавливаем преобразование с ctx.setTranform заменим существующие преобразования. Это преобразование остается актуальным. Если мы используем ctx.transform, ctx.rotate, ctx.scale, ctx.translate, преобразования применяются к текущему преобразованию, вы строите преобразование поэтапно.

Функции преобразования относительно дороги с точки зрения циклов ЦП. Таким образом, использование sin и cos для построения матрицы намного быстрее, чем использование ctx.scale, ctx.rotate, ctx.translate для выполнения того же действия, начиная со значения по умолчанию.

Построение преобразований может стать непростым делом, как нам нужно чтобы отслеживать, на какой стадии мы находимся.

Обычно мы используем эти функции не для преобразования одного изображения (текста, пути или чего-либо еще), а для создания связанных преобразований.

Например, игровой объект, такой как танк. Корпус танка трансформируется (поворачивается и позиционируется), а затем револьверная головка, которая вращается вместе с корпусом, но имеет дополнительное независимое вращение с помощью ctx.rotate. Полное объяснение выходит за рамки этого вопроса.

Последняя функция

Из всего этого мы можем создать упрощенную функцию, которая будет рисовать изображение с центром в любом месте, которое будет равномерно масштабировано. и повернут

 function drawImageScaleRotate(img, x, y, scale, rotate) {
      const xAX = Math.cos(rotate) * scale;
      const xAY = Math.sin(rotate) * scale;
      ctx.setTransform(xAX, xAY, -xAY, xAX, x, y);
      ctx.drawImage(img, -img.width / 2, -img.height / 2);
   }

Для сброса преобразования к использованию по умолчанию ctx.resetTransform ПРИМЕЧАНИЕ еще не полностью поддерживается или используйте ctx.setTransform(1,0,0,1,0,0);

Анимированные преобразования быстрее, чем CSS + HTML или SVG

Использование вышеуказанной функции является вторым самым быстрым способом рисования анимированных повернутых масштабированных изображений, быстрее, чем CSS + HTML или SVG. Вы можете буквально заполнить экран анимированными изображениями.

Демо

var w,h;
var image = new Image;
image.src = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
document.body.appendChild(canvas);
const resize = () => { w = canvas.width = innerWidth; h = canvas.height = innerHeight;}
const rand = (min,max) => Math.random() * (max ?(max-min) : min) + (max ? min : 0);
const DO = (count,callback) => { while (count--) { callback(count) } }

resize();
addEventListener("resize",resize);

const sprites = [];
DO(500,()=>{
    sprites.push({
       xr : rand(w), yr : rand(h),
       x : 0, y : 0, // actual position of sprite
       r : rand(Math.PI * 2),
       scale : rand(0.1,0.25),
       dx : rand(-2,2), dy : rand(-2,2),
       dr : rand(-0.2,0.2),
    });
});

function drawImage(image, spr){
    const xAX = Math.cos(spr.r) * spr.scale;
    const xAY = Math.sin(spr.r) * spr.scale;
    ctx.setTransform(xAX, xAY, -xAY, xAX, spr.x, spr.y); 
    ctx.drawImage(image, -image.width / 2, -image.height / 2);
}

function update(){
    var ihM,iwM;
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,w,h);
    if(image.complete){
      var iw = image.width;
      var ih = image.height;
      for(var i = 0; i < sprites.length; i ++){
          var spr = sprites[i];
          spr.xr += spr.dx;
          spr.yr += spr.dy;
          spr.r += spr.dr;
          // keeps images in canvas adds space to all sides so that image
          // can move completely of the canvas befor warping to other side
          // I do this to prevent images visualy popping in and out at edges
          iwM = iw * spr.scale * 2 + w;
          ihM = ih * spr.scale * 2 + h;
          spr.x = ((spr.xr % iwM) + iwM) % iwM - iw * spr.scale;
          spr.y = ((spr.yr % ihM) + ihM) % ihM - ih * spr.scale;
          
          drawImage(image,spr);
      }
    }    
    requestAnimationFrame(update);
}
requestAnimationFrame(update);

Если вам интересно, какой самый быстрый способ рисовать анимированный контент. Это через webGL. Выше можно нарисовать 1000 масштабированных повернутых изображений на большинстве устройств с хорошей частотой кадров. WebGL может легко рисовать 10000 (с дополнительными функциями, например, цветными) одновременно.

0 голосов
/ 16 февраля 2020

У вас есть функция ctx.rotate(radians). Читайте ниже:

https://www.w3schools.com/tags/canvas_rotate.asp

0 голосов
/ 16 февраля 2020

используйте преобразование "вращать". И используйте его как показано ниже

p{
  color:red;
  font-size:12px;
  text-align:center;
}

.rotate1{
  transform:rotate(45deg);
    margin-top:40px;
}

.rotate2{
  transform:rotate(90deg);
  margin-top:40px;
}

.rotate3{
  transform:rotate(180deg);
  margin-top:40px;
}
<p class="rotate1">ROTATE1</p>
<p class="rotate2">ROTATE2</p>
<p class="rotate3">ROTATE3</p>
0 голосов
/ 15 февраля 2020

Вы можете легко вращать изображение. Вы можете указать углы, к которым вы хотите применить вращение.

Источник: https://www.w3schools.com/cssref/css3_pr_transform.asp

<style>
    img.a {
        transform: rotate(180deg);
    }
</style>
<img  class="a" src="https://picsum.photos/id/237/200/300"/>
...