Увеличение изображения до разрыва курсора при перемещении мыши

Это дополнительный вопрос к Как приблизить указатель мыши при использовании моей собственной плавной прокрутки колесика мыши?

Я использую преобразования css, чтобы приблизить изображение к указателю мыши. Я также использую свой собственный алгоритм плавной прокрутки для интерполяции и придания импульса колесу мыши.

С помощью Бали Бало в моем предыдущем вопросе мне удалось пройти 90% пути.

Теперь вы можете полностью приблизить изображение к указателю мыши, сохраняя плавную прокрутку, как показано на следующем примере JSFiddle:

http://jsfiddle.net/qGGwx/7/

Однако при перемещении указателя мыши функциональность нарушается.

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

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

Две основные функции, управляющие текущим поведением, следующие:

self.container.on('mousewheel', function (e, delta) {
        var offset = self.image.offset();

        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;

        if (!self.running) {
            self.running = true;
            self.animateLoop();
        }
        self.delta = delta
        self.smoothWheel(delta);
        return false;
    });

Эта функция собирает текущее положение мыши в текущем масштабе увеличенного изображения.

Затем он запускает мой алгоритм плавной прокрутки, который приводит к вызову следующей функции для каждой интерполяции:

zoom: function (scale) {

    var self = this;
    self.currentLocation.x += ((self.mouseLocation.x - self.currentLocation.x) / self.currentscale);
    self.currentLocation.y += ((self.mouseLocation.y - self.currentLocation.y) / self.currentscale);

    var compat = ['-moz-', '-webkit-', '-o-', '-ms-', ''];
    var newCss = {};
    for (var i = compat.length - 1; i; i--) {
        newCss[compat[i] + 'transform'] = 'scale(' + scale + ')';
        newCss[compat[i] + 'transform-origin'] = self.currentLocation.x + 'px ' + self.currentLocation.y + 'px';
    }
    self.image.css(newCss);


    self.currentscale = scale;
},

Эта функция принимает значение масштаба (1-10) и применяет преобразования css, изменяя положение изображения с помощью transform-origin.

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

Заранее огромное спасибо всем, кто может помочь.


person gordyr    schedule 21.01.2013    source источник
comment
демо, похоже, работает нормально в соответствии с вашим описанием. пожалуйста, подробно опишите проблему. что вы имеете в виду под правильно отраженным?   -  person Eliran Malka    schedule 23.01.2013
comment
Масштаб увеличения установлен от 1 до 10. Попробуйте увеличить масштаб только частично, скажем, примерно до 3 с помощью колесика мыши. Затем переместите указатель мыши на другой пиксель и продолжите масштабирование до конца. Вы увидите, что он неправильно приближается к НОВОМУ положению мыши. Источник масштабирования сохраняет правильное положение только тогда, когда указатель мыши остается неподвижным в течение всего масштабирования, он должен правильно изменять положение при перемещении мыши.   -  person gordyr    schedule 23.01.2013
comment
хорошо, я понял, так что правильным поведением будет масштабирование до новой точки (извлеченной из текущего уровня шкалы), а не относительного местоположения на изображении в масштабе 1. Я еще там?   -  person Eliran Malka    schedule 23.01.2013
comment
Совершенно верно ... Мои извинения за то, что не разъяснили это.   -  person gordyr    schedule 23.01.2013


Ответы (5)


Прежде чем проверить эту скрипку; Я должен упомянуть:

Прежде всего в рамках вашего .zoom() метода; не делите на currentscale:

self.currentLocation.x += ((self.mouseLocation.x - self.currentLocation.x) / self.currentscale);
self.currentLocation.y += ((self.mouseLocation.y - self.currentLocation.y) / self.currentscale);

так как; вы уже используете этот коэффициент при вычислении mouseLocation внутри метода initmousewheel() следующим образом:

self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;

Так что вместо этого; (в методе .zoom()) следует:

self.currentLocation.x += (self.mouseLocation.x - self.currentLocation.x);
self.currentLocation.y += (self.mouseLocation.y - self.currentLocation.y);

Но (например) a += b - a всегда будет давать b, поэтому приведенный выше код равен:

self.currentLocation.x = self.mouseLocation.x;
self.currentLocation.y = self.mouseLocation.y;

короче:

self.currentLocation = self.mouseLocation;

Тогда, кажется, тебе даже не нужно self.currentLocation. (2 переменные для одного значения). Так почему бы не использовать переменную mouseLocation в строке, где вы устанавливаете transform-origin, и избавиться от переменной currentLocation?

newCss[compat[i] + 'transform-origin'] = self.mouseLocation.x + 'px ' + self.mouseLocation.y + 'px';

Во-вторых, вы должны включить прослушиватель событий mousemove в метод initmousewheel() (как и предлагают здесь другие разработчики), но он должен обновлять преобразование постоянно, а не только тогда, когда пользователь движется. В противном случае кончик указателя никогда не догонит, пока вы уменьшаете масштаб "любой" случайной точки.

self.container.on('mousemove', function (e) {
    var offset = self.image.offset();
    self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
    self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
    self.zoom(self.currentscale);
});

Так; вам больше не нужно вычислять это в обработчике событий mousewheel, поэтому ваш метод initmousewheel() будет выглядеть так:

initmousewheel: function () {
    var self = this;

    self.container.on('mousewheel', function (e, delta) {
        if (!self.running) {
            self.running = true;
            self.animateLoop();
        }
        self.delta = delta;
        self.smoothWheel(delta);
        return false;
    });

    self.container.on('mousemove', function (e) {
        var offset = self.image.offset();
        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
        self.zoom(self.currentscale); // <--- update transform origin dynamically
    });
}

Одна проблема:

Это решение работает должным образом, но с небольшой проблемой. Когда пользователь перемещает мышь с обычной или высокой скоростью; событие mousemove, похоже, не принимает окончательную позицию (проверено в Chrome). Таким образом, масштабирование будет немного отличаться от положения указателя. В противном случае, когда вы медленно перемещаете мышь, она получает точную точку. Однако это должно быть легко обойти.

Другие примечания и предложения:

  • У вас есть повторяющийся объект недвижимости (prevscale).
  • Я предлагаю вам всегда использовать JSLint или JSHint (который также доступен на jsFiddle) для проверки вашего кода.
  • Я настоятельно рекомендую вам использовать замыкания (часто называемые выражением немедленно вызываемой функции (IIFE)), чтобы по возможности избежать глобальной области видимости; и скрыть свои внутренние / частные свойства и методы.
person Onur Yıldırım    schedule 12.02.2013
comment
Фантастика!!! Великолепно написанный ответ, заслуживающий награды. Ваши комментарии относительно самого кода полностью верны, и я ценю их, хотя сам jsFiddle не является фактическим кодом, который я использую в приложении, это был быстрый упрощенный макет, который я написал для демонстрационных целей, и поэтому в перевод. Несмотря на это, мне действительно следует привыкнуть использовать предоставленный JSLint в jSFiddle. Опять же, я искренне не могу вас отблагодарить. :) - person gordyr; 12.02.2013
comment
Совершенно никаких проблем. Рад помочь. :) - person Onur Yıldırım; 12.02.2013

На самом деле, не так уж и сложно. Вам просто нужно отделить логику обновления местоположения мыши от логики обновления масштабирования. Посмотрите мою скрипку: http://jsfiddle.net/qGGwx/41/

Все, что я здесь сделал, это добавил слушателя mousemove в контейнер и поместил туда логику обновления self.mouseLocation. Поскольку это больше не требуется, я также вынул логику обновления mouseLocation из обработчика mousewheel. Код анимации остается прежним, как и решение о том, когда запускать / останавливать цикл анимации.

вот код:

    self.container.on('mousewheel', function (e, delta) {
        if (!self.running) {
            self.running = true;
            self.animateLoop();
        }
        self.delta = delta
        self.smoothWheel(delta);
        return false;
    });

    self.container.on('mousemove', function (e) {
        var offset = self.image.offset();

        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;
    });
person jordancpaul    schedule 27.01.2013
comment
Спасибо jordancpaul, это очень близко. Однако, хотя новое положение масштабирования теперь правильное, кажется, что все это отодвигается от указателя мыши, что означает, что масштаб не совпадает с точным расположением мыши. Извините, это немного сложно сформулировать. На данный момент вы предоставили наиболее близкий ответ, но я собираюсь оставить его открытым еще немного, на случай, если кто-то сможет получить его идеально, в противном случае я награжу вас ответом. Несмотря на отличную работу, спасибо! - person gordyr; 27.01.2013

Добавьте метод mousemover и вызовите его в методе init:

mousemover: function() {
    var self = this;
    self.container.on('mousemove', function (e) {

        var offset = self.image.offset();

        self.mouseLocation.x = (e.pageX - offset.left) / self.currentscale;
        self.mouseLocation.y = (e.pageY - offset.top) / self.currentscale;

        self.zoom(self.currentscale);
    });
},

Скрипка: http://jsfiddle.net/powtac/qGGwx/34/

person powtac    schedule 23.01.2013
comment
Спасибо, powtac ... Боюсь, это не сработает, как и Fiddle. Поведение осталось прежним. Однако отслеживание изменений положения мыши внутри события mousemove, а не внутри события mousewheel, похоже, может быть интересным маршрутом. Ваше здоровье. - person gordyr; 23.01.2013
comment
Ааа, возможно, вы неправильно поняли вопрос, мои извинения. Изображение не должно двигаться при перемещении мыши. он должен изменяться / масштабироваться только с помощью колеса мыши .... Оставаясь неподвижным, пока изменяется положение мыши, затем возобновляйте масштабирование с НОВОГО положения мыши, если пользователь продолжает увеличивать масштаб. Надеюсь, что это проясняет ... Спасибо за хотя пытаюсь! - person gordyr; 23.01.2013

Точка увеличения не совсем правильная из-за масштабирования изображения (0.9 в ratio). Фактически мышь указывает на конкретную точку в container, но мы масштабируем image. См. Эту скрипку. http://jsfiddle.net/qGGwx/99/ Я добавляю marker с позицией, равной transform-origin. Как видите, если размер изображения равен размеру контейнера, проблем нет. Вам нужно это масштабирование? Может, можно добавить второй контейнер? В скрипке я также добавил условие в mousemove

if(self.running && self.currentscale>1 && self.currentscale != self.lastscale) return;

Это предотвращает перемещение изображения во время масштабирования, но также создает проблему. Вы не можете изменить точку масштабирования, если zoom все еще работает.

person abc667    schedule 08.02.2013
comment
Спасибо abc667. К сожалению, да, масштабирование необходимо, поскольку размер изображения никогда не будет известен заранее и масштабируется для заполнения экрана через CSS. Есть ли способ обойти это? - person gordyr; 08.02.2013
comment
Я много тестировал это и думаю, что на самом деле проблем нет. В этой скрипке jsfiddle.net/qGGwx/102 я добавляю вторую картинку и устанавливаю положение зеленого маркера. преобразовать происхождение, и все в порядке. Если вы увеличите масштаб от scale=1 до некоторой точки, эта точка не будет центрирована. То же самое происходит, если вы меняете точку масштабирования, и это выглядит как ошибка, но это не так. Если вы хотите, чтобы это выглядело правильно, вам нужно отцентрировать точку масштабирования на основе расстояния от центра. Но затем, если вы увеличите масштаб до некоторого угла, вы получите 3/4 контейнера черного цвета. - person abc667; 10.02.2013

Расширяя ответ @jordancpaul, я добавил константу mouse_coord_weight, которая умножается на дельту координат мыши. Это нацелено на то, чтобы сделать переход масштабирования менее чувствительным к изменению координат мыши. Проверьте это http://jsfiddle.net/7dWrw/

Я переписал обработчик событий onmousemove как:

    self.container.on('mousemove', function (e) {
        var offset = self.image.offset();
        console.log(offset);

        var x = (e.pageX - offset.left) / self.currentscale,
            y = (e.pageY - offset.top) / self.currentscale;

        if(self.running) {
            self.mouseLocation.x += (x - self.mouseLocation.x) * self.mouse_coord_weight;
            self.mouseLocation.y += (y - self.mouseLocation.y) * self.mouse_coord_weight;
        } else {
            self.mouseLocation.x = x;
            self.mouseLocation.y = y;
        }

    });
person Birla    schedule 10.02.2013
comment
@gordyr Удачи с этим? - person Birla; 12.02.2013