#javascript #animation #trigonometry
#javascript #Анимация #тригонометрия
Вопрос:
Я работаю над своим первым проектом canvas, и для этого требуется частичная карта США с масштабированием и центром по состоянию при нажатии.
Мне удалось найти массивы точек X Y для рисования страны, причем каждое государство является собственным массивом. Мне нужно было, чтобы состояния были вытянуты больше, чем эти размеры, поэтому я ввел переменную шкалу для умножения каждой точки.
Моя следующая проблема заключалась в том, что клиент хотел, чтобы было выделено только 13 состояний, но не размещалось для масштабирования друг против друга. (Например, поместите Огайо и Иллинойс рядом друг с другом на холсте и игнорируйте Индиану). Мое решение состояло в том, чтобы ввести фиксированную «константу» X, Y для каждого состояния, чтобы после масштабирования добавить значение X Y для этого состояния и сделать его местом для рисования.
for ( var j = 0; j < state.myPolygons.length; j) {
context.beginPath();
context.lineWidth = lineWidth;
context.strokeStyle = stateStroke;
context.fillStyle = stateFill;
for ( var k = 0; k < state.myPolygons[j].myXVals.length; k ) {
var x = parseFloat(state.myPolygons[j].myXVals[k]*state.scale) state.posX;
var y = parseFloat(state.myPolygons[j].myYVals[k]*state.scale) state.posY;
y = canvas.height - y;
if ( k == 0 )
context.moveTo(x,y);
else
context.lineTo(x,y);
}
context.closePath();
context.fill();
context.stroke();
}
Эффект нажатия на состояние, его увеличения и центрирования на холсте был достигнут путем определения целевого масштаба и количества шагов. Я получаю разницу между целевым масштабом и текущим масштабом и делю это на количество шагов, чтобы выяснить, сколько нужно добавить к масштабу состояния в каждом «кадре».
Пример: начальный масштаб Огайо равен 1,97 от найденных координат. Моя цель для шкалы Огайо — 3,75%. Я получаю разницу (1,78) и делю ее на 45 (определенный набор шагов) для рисования. Это дает мне 0,039 в качестве приращения к моему масштабу в каждом кадре. Затем я выполняю цикл, пока текущий масштаб моих состояний меньше целевого масштаба. Однако, опять же, поскольку мне нужно манипулировать Xy рендеринга, у меня есть константа zoomx и zoomy для каждого состояния, которое добавляется к вычисленному Xy, чтобы оно могло «скользить» к центру холста.
Все это работает отлично, и у меня есть калифорнийский масштаб / сдвиг слева направо, Огайо сдвиг справа налево и т.д. — Вот моя проблема.
У меня есть ряд точек для обозначения местоположения клиента в состоянии. Это простые Xy, на которых я рисую круг. Начальный рендеринг карты включает в себя цикл для выполнения через каждый набор состояний местоположений. Я применяю тот же масштабный коэффициент и переменные PosX, posY для настройки окончательного размещения точки по отношению к окончательному отображению состояния
for (var loc in state.Locations) {
var locx = parseFloat(state.Locations[loc].x*state.scale) state.posX
var locy =parseFloat(state.Locations[loc].y*state.scale) state.posY;
var txt=state.Locations[loc].text;
var lnk=state.Locations[loc].link;
context.beginPath();
context.arc(locx,locy,locationSize,0,Math.PI*2,true);
context.fillStyle = locationFill;
context.closePath();
context.fill();
context.stroke();
}
Однако, когда состояние масштабируется, логика масштабирования для точек завершается сбоем. Применяется масштаб состояния для данного фрейма
x = parseFloat(activeState.myPolygons[j].myXVals[k]*activeState.scale) activeState.posX;
y = parseFloat(activeState.myPolygons[j].myYVals[k]*activeState.scale) activeState.posY;
Когда я применяю это к заданному местоположению в состоянии с
locx = parseFloat(activeState.Locations[loc].x*activeState.scale) activeState.posX;
locy = parseFloat(activeState.Locations[loc].y*activeState.scale) activeState.posY;
В итоге X следует довольно точно, но в примере с Огайо Y находится где-то рядом с Флоридой. В других штатах, таких как Калифорния, дела обстоят еще хуже, поскольку их точки начинают более «укладываться» друг на друга и в конечном итоге оказываются более «разбросанными» рядом друг с другом.
Я пытаюсь определить тригонометрические функции, необходимые для увеличения и уменьшения положения X Y в местоположении по отношению к текущему масштабу состояния, и сохранить его на том же пути, по которому состояние проходит через анимацию (как увеличение, так и уменьшение масштаба).
Моей последней попыткой перед тем, как прийти сюда, было получить начальный Xy местоположения и сравнить его расстояние с ПОСЛЕДНИМ Xy массива состояний. Затем я пытался найти угол линии, соединяющей эти 2 точки, а затем использовать все это для масштабирования. Я все еще чувствую, что могу быть на что-то с этим подходом, я просто не могу этого добиться.
Спасибо всем, что нашли время, чтобы прочитать это, я ценю любую помощь, которую вы можете предложить
Комментарии:
1. Вы вообще рассматривали возможность использования SVG для этого? Все это масштабирование и анимация встроены в SVG.
2. Спасибо, к сожалению, в этом проекте эти функции были расширены, и я слишком далеко продвинулся по этому пути, чтобы повернуть назад. Первоначально он должен был нарисовать состояние «маленький», а затем «большой» — анимация была введена где-то в середине этой цели. Я выбрал canvas для первоначальной цели как возможность получить опыт работы с ним, и теперь я сталкиваюсь с ГОРАЗДО большим опытом, чем предполагал 🙂
Ответ №1:
Вы могли бы просто взглянуть на бумагу, которую я положил на ваш стол, на ту, на которой написано уравнение. Однако SVGS был бы более оптимальным для проекта, поскольку вы могли бы легко группировать объекты вместе, используя тег g, а затем просто масштабировать всю группу.
Однако, поскольку на этом этапе вы вынуждены использовать canvas: вам придется масштабировать director вверх и вниз, используя тригонометрию, учитывая угол начальной точки до точки местоположения и РАЗНИЦУ пройденного влево или вправо от исходного расстояния. Я объясню более подробно, с реальными уравнениями, когда вы позволите мне вернуть мне эту бумагу. Однако единственная строка, которую вам действительно нужно изменить на этом этапе, это:
locy = parseFloat(activeState.Locations[loc].y*activeState.scale) activeState.posY;