Резюме проекта Vue + Canvas

Vue.js Canvas
Резюме проекта Vue + Canvas

демонстрационный адрес

демонстрационный адрес
Проект на стороне ПК нужно просматривать на компьютере, и лучше всего открывать его с помощью Chrome

введение

Это проект, который я сделал для пожилых людей в марте этого года. Я провел со мной два месяца весеннего набора. Я многому научился за весь проект. Теперь я начну делиться своими знаниями, в том числе кратким изложением знаний и опытом. - Далао Мо рассмеялся.

Обзор проекта

功能展示
Основные функции показаны выше, слева — панель графических инструментов, справа — холст, сверху — операции очистки, удаления, поворота, переключения фона сетки, сохранения и загрузки изображений.

Код основан на коде vue-cli, поэтому про роутинг и vuex говорить не приходится, сосредоточимся на canvas.

Резюме очков знаний

тянуть

Под перетаскиванием здесь понимается перетаскивание графики с левой панели инструментов на правый холст, которое выполняется в три этапа:

  1. настройки перетаскиваемого элементаdraggable="true";
  2. Также есть три соответствующих события для перетаскиваемого элемента.dragstart drag dragend, соответствующие началу перетаскивания, середине перетаскивания и концу перетаскивания соответственно.Если вы хотите добавить к этим процессам специальные эффекты, вы можете попробовать это, но больше используется в качестве данных ответа, например, позволяя холсту знать, какой элемент перетаскивается.
  3. Настройки размещенного элементаdragover dropДва события, соответственно указывающие на то, что перетаскиваемый элемент перемещается в пределах области действия элемента, и перетаскиваемый элемент приземляется, обратите внимание здесьdragoverФункция события должна быть установленаevent.preventDefault()Предотвратить всплывающую страницу, то мы можем с радостьюdropФункция события рисует графику на холсте.

HEX => RGBA

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

function hex2rgba(hex) {
      // hex格式如#ffffff
      let colorArr = [];
      for(let i = 1; i<7; i += 2){
        colorArr.push(parseInt("0x" + hex.slice(i,i+2))); // 16进制值转10进制
      }
      return `rgba(${colorArr.join(",")},0.3)`;
}

Кроме того, если вам интересно узнать о преобразовании RGBA в RGB, вы можете посетить этот блог.Преобразование RGBA в RGB

Основное использование холста

Ниже приведено содержимое холста, если вы мало знаете о его основном использовании, вы можете взглянутьХолст JavaScript

сохранить и восстановить

saveСостояние текущего холста можно сохранить, в том числеstrokeStyle,fillStyle, матрицы преобразования, области отсечения и т. д.,restoreЕго можно восстановить до предыдущего состояния в стеке состояний холста, поэтому изменение состояния холста, которое мы сделали между этими двумя функциями, эквивалентно изоляции.Не загрязняет внешние операции холста.

Таким образом, нам лучше звонить перед каждым розыгрышемsave, звоните после розыгрышаrestore, тем самым гарантируя чистое состояние для каждого розыгрыша.

Вот особенно хорошо написанная статья.Если вы думаете, что этот прямой человек не объяснил это ясно, вы должны прочитать ее.Изучение холста: save() и restore()

drawImage

Некоторые друзья могут недооценивать эту API, думая, что он может рисовать только картинки, но на самом деле это может такжесвг, холстРисуйте на холсте, давайте сначала посмотрим, как нарисовать svg.

Значки на панели инструментов в левой части нашего интерфейса функций на самом деле svg. Сначала я хотел сделать их скриншоты и вырезать их в png с прозрачным фоном, а затем нарисовать их на холсте. Пиксмапы, поэтому появляются новые требования. .

Мой собственный код не так просто опубликовать, так что взгляните на код Далао,Нарисуйте объекты DOM на холсте, он запихивает DOM в svg и отрисовывает его на канве.Если нужно только отрисовать готовый svg, то можноforeignObjectпакет.

Кроме того, если ваш svg имеет изображения формата .svg, вы можете напрямую вызватьdrawImageрисовать.

Эллипсы и кривые Безье

В Canvas уже есть API для рисования эллипсов, но совместимость не на высоте.Среди всех остальных способов имитации рисования эллипсов кривые Безье можно сказать самые изящные, ну и грамотность =>Принцип кривой Безье (простое объяснение)

Трехмерная кривая Безье должна определяться начальной точкой, двумя промежуточными точками и конечной точкой.Конечно, начальная точка обычно по умолчанию является текущей точкой, поэтомуbezierCurveToПараметры являются координатами последних трех точек по порядку; когда эти четыре точки просто прилагают прямоугольник, он выглядит как эллипс.

 let a = this.width / 2;
 let b = this.height / 2;
 let ox = 0.5 * a,
     oy = 0.6 * b;
 this.ctx.beginPath();
 // 从椭圆纵轴下端开始逆时针方向绘制
 this.ctx.moveTo(0, b);
 // 把椭圆划成四份分开来画
 this.ctx.bezierCurveTo(ox, b, a, oy, a, 0);
 this.ctx.bezierCurveTo(a, -oy, ox, -b, 0, -b);
 this.ctx.bezierCurveTo(-ox, -b, -a, -oy, -a, 0);
 this.ctx.bezierCurveTo(-a, oy, -ox, b, 0, b);
 this.ctx.closePath();
 this.ctx.fill();

Вот статья об относительно полном методе рисования эллипса, на который вы можете сослаться.5 способов нарисовать эллипс в холсте HTML5

линия

сплошная линия со стрелкой

Сплошные линии рисовать легко, а как насчет стрелок? Эммм, по сути, заключается в том, чтобы вычислить угол между отрезком линии и осью x холста, а затем нарисовать треугольник со смещением, соответствующим конечной точке отрезка линии.

drawArrow(x1, y1, x2, y2) {
    // (x1, y1)是线段起点  (x2, y2)是线段终点
    // 反正切函数计算夹角
    let endRadians = Math.atan((y2 - y1) / (x2 - x1));
    // 三角形的底边与线段垂直,所以还要再转 π / 2
    endRadians += ((x2 >= x1) ? 90 : -90) * Math.PI / 180;
    this.ctx.save();
    this.ctx.beginPath();
    // 坐标原点 => (x2, y2)
    this.ctx.translate(x2, y2);
    this.ctx.rotate(endRadians);
    this.ctx.moveTo(0, 0);
    this.ctx.lineTo(5, 15);
    this.ctx.lineTo(-5, 15);
    this.ctx.closePath();
    this.ctx.fill();
    this.ctx.restore();
}

пунктирная линия

  • Более традиционный подход заключается в изменении прототипа CanvasRenderingContext2D, ручном увеличении dashedLine методом, принцип, вероятно, начинается отрисовка с точки сплошной линии, и пропустить, следующая точка для перехода к продолжению рисования сплошной линии, завершение цикла точка, ломаная линия может быть получена. См. конкретную реализациюНарисованная линия реализации HTML5
  • На самом деле холст уже поддерживает рисование пунктирных линий, используйте его перед рисованием линийsetLineDashВы можете указать стиль пунктирной линии, см.Изучение холста: рисование пунктирных и пунктирных линий
    Однако у этого метода есть некоторые проблемы: при неудачном угле или слишком маленьком интервале нарисованная пунктирная линия выглядит сплошной.

волнистая линия

Используются обычно используемые волнистые линиисинусоидальныйСмоделируем его, y = A * sin(ω * x + φ), задав его A и ω, можно определить амплитуду и частоту волновой линии (или высоту и ширину каждой волны)

let len = Math.sqrt(width * width + height * height);
this.ctx.save();
this.ctx.moveTo(this.start.x,this.start.y); // 起点
this.ctx.translate(this.start.x,this.start.y);
this.ctx.beginPath();
let x = 0;
let y = 0;
let amplitude = 5; // 振幅
let frequency = 5; // 频率
while (x < len) {
    y = amplitude * Math.sin(x / frequency);
    this.ctx.lineTo(x, y);
    x = x + 1;
}
this.ctx.stroke();
this.ctx.restore();

Справочная статья:Draw a Sine Wave in JavaScript

графический стек

спасти

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

удалять

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

вращать

Поскольку каждый раз, когда мы рисуем графику, мы временно перемещаем начало координат в центр графики, поэтому нам нужно толькоrotateПовернуть, рисуя под углом

Перетаскивать

Эммм, каждая графика не слишком, если вам интересно, проверьте исходный код проекта

Проверить, находится ли точка внутри четырехугольника

  • векторный метод
    смотрите подробностиПроверить, находится ли точка внутри четырехугольника, но этот метод немного ограничен, во-первых,Количество ребер графа должно быть определено заранееи код будет очень длинным, если количество ребер увеличивается; во-вторых, этот метод подходит только длявыпуклый многоугольник, вы можете понять это на встречном примере с вогнутым многоугольником.
  • лучевой метод
    смотрите подробностиТеория лучевого метода, код реализован следующим образом:
function inRange(x, y, points){
    // points表示多边形的顶点集合
    let inside = false;
    for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
        let xi = points[i][0], yi = points[i][1];
        let xj = points[j][0], yj = points[j][1];
        let intersect = ((yi > y) !== (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }
    return inside;
}
  • формула
    Для любой точки (x, y) новая координата после поворота на угол против часовой стрелки вокруг точки координат (rx0, ry0) устанавливается равной (x0, y0) по формуле:
    x0= (x - rx0)*cos(a) - (y - ry0)*sin(a) + rx0 ;
    y0= (x - rx0)*sin(a) + (y - ry0)*cos(a) + ry0 ;
    Знание полярных координат, если не хотите навязывать, просто задайте формулу напрямую.

Отмена и откат

Функция похожа на PS, я не делал этот проект, но идея не сложна, используйте прошлое, настоящее, будущее три массива для сохранения графического стека, Эмм кажется немного длинным, вы можете обратиться кРеализовать историю отменидеи.

приоритет

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

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

Состояние в vuex реализует двустороннюю привязку

Как правило, мы используем значение двусторонней привязки, которое будет помещено в экземпляр Vue.dataпотому что он обеспечивает по умолчаниюgetterа такжеsetter; но состояние vuex обычно требуетcomputedчитать, ноcomputedМетода установки по умолчанию нет, и его нужно задать вручную, код такой:

computed:{
      text : {
        get(){
          return this.$store.state.text;
        },
        set(value){
          this.$store.commit('setText',value);
        }
      }
}

яма столкнулась

Небольшая ошибка в html2canvas.

При внедрении сохранения изображения функции, я надеюсь перехватить содержание некоторых из DOM, а не только содержанием холста, поэтому найти этот штекер HTML2CANVAS, он может преобразовать в холст-дом, тогда мы сможемcanvas.toDataURL()Превратите его в картинку.

Код для преобразования и сохранения в качестве загрузки изображения выглядит следующим образом:

downImg() {
        html2canvas( this.$refs.ground, {
          onrendered: function(canvas) {
            let url = canvas.toDataURL();
            let a = document.createElement('a');
            a.href = url;
            a.download = new Date() + ".png";
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
          }
        });
    }

Но есть баг, то есть скачанная картинка не четкая, а в левом верхнем углу большой пробел.
Поэтому я перепробовал много методов в Интернете, но это не сработало, в итоге я смог только медленно добавлять вещи в проект с нуля и, наконец, узнал, что это я.Изменен прототип CanvasRenderingContext2D при рисовании пунктирной линии., я мама, никогда не мечтала, что тут будет проблема, рискованно пользоваться плагинами.

Неверный путь при загрузке на gh-pages

Если заливать на https://XXX.github.io/ (личный блог GitHub), то это тоже самое, что заливать на сервер, а вот если заливать на gh-страницы склада, то куча проблем придет , шаги решения следующие:

  1. Пучок.gitignoreв файле/distЕсли вы удалите его, если вы его проигнорируете, как вы можете загрузить упакованный файл в основную ветку?
  2. /config/index.jsв разделе сборкиassetsPublicPathИзменение с '/' на './' эквивалентно изменению корневого каталога сервера на относительный путь, а корневым каталогом gh-страниц хранилища является не '/', а '/имя хранилища';
  3. Для сравнения, если вы используете режим истории, измените его на свой режим хеширования, иначе GitHub может использовать интерфейсный маршрут к серверному API;
  4. осталось немногоstaticКартинка в файле использует абсолютный путь, который может не отображаться после загрузки;
  5. git subtree push --prefix dist origin gh-pagesПосле ввода команды вы должны увидеть, что загрузка прошла успешно.

оптимизация

Слоистый холст

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

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

закадровый рендеринг

Когда мы перетаскиваем графику на холст, обычно мы перемещаем ее мышью.mousemove, перерисовывать всю графику, но на самом деле в этом процессе рисуемая графика может быть разделена на две части, одна — это графика, которую перетаскивают и перемещают, а другая — это другая графика, соответственно мы можем динамически создавать два холста , и нарисуйте две части на двухЗакадровый холстначальство,mousemoveВам нужно только дважды вызвать drawImage (закадровый холст), так что это стоит много производительности?

кодовый адрес

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