22

これは、独自のマウスホイールのスムーズスクロールを使用しているときにマウスポインターにズームする方法へのフォローアップの質問です?

css 変換を使用して、画像をマウス ポインターにズームしています。また、独自のスムーズ スクロール アルゴリズムを使用して補間し、マウスホイールに勢いを与えています。

前回の質問でバリ バロの助けを借りて、私は 90% の方法を得ることができました。

次の JSFiddle が示すように、スムーズなスクロールを維持しながら、マウス ポインターまで画像をズームできるようになりました。

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

ただし、マウス ポインターを移動すると、機能が壊れます。

さらに明確にするために、マウスホイールの 1 つのノッチを拡大すると、画像は正しい位置にズームされます。この動作は、マウスホイールでズームインするたびに、完全に意図したとおりに続きます。ただし、途中でズームした後にマウスを別の位置に移動すると、機能が壊れ、ズーム位置を変更するには完全にズームアウトする必要があります。

意図された動作は、ズーム プロセス中のマウス位置の変更が、ズームされた画像に正しく反映されるようにすることです。

現在の動作を制御する 2 つの主な関数は次のとおりです。

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 を使用して画像を再配置します。

これは、画像が完全に縮小されたときに選択された静止マウス位置では完全に機能します。上記のように、部分ズーム後にマウスカーソルを移動すると壊れます。

助けていただける方には、あらかじめ多大な感謝を申し上げます。

4

5 に答える 5

8

実際、それほど複雑ではありません。マウスの位置更新ロジックをズーム更新ロジックから分離する必要があります。私のフィドルをチェックしてください:http: //jsfiddle.net/qGGwx/41/

ここで行ったのは、コンテナーに「mousemove」リスナーを追加し、そこにself.mouseLocation更新ロジックを配置することだけです。不要になったため、「mousewheel」ハンドラーからmouseLocation更新ロジックも削除しました。アニメーションループを開始/停止するタイミングの決定と同様に、アニメーションコードは同じままです。

コードは次のとおりです。

    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;
    });
于 2013-01-27T09:14:13.213 に答える
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-origincurrentLocation

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
    });
}

1 つの問題:

このソリューションは期待どおりに機能しますが、小さな問題があります。ユーザーが通常または高速でマウスを動かしたとき。mousemoveイベントは最終的な位置を逃しているようです (Chrome でテスト済み) 。そのため、ズームはポインターの位置から少しずれます。それ以外の場合は、マウスをゆっくり動かすと、正確なポイントが得られます。ただし、これを回避するのは簡単なはずです。

その他の注意事項と提案:

  • 重複したプロパティ ( prevscale) があります。
  • コードを検証するには、常にJSLintまたはJSHint (jsFiddle でも利用可能) を使用することをお勧めします。
  • 可能であればクロージャ ( Immediately Invoked Function Expression (IIFE) と呼ばれることが多い) を使用して、グローバル スコープを回避することを強くお勧めします。内部/プライベート プロパティとメソッドを非表示にします。
于 2013-02-12T05:04:00.123 に答える
4

メソッドを追加し、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/

于 2013-01-23T10:59:35.877 に答える
3

画像のスケーリングのため、ズーム ポイントが正確ではありません ( 0.9) ratio。実際、マウスは特定のポイントを指していますが、containerスケーリングしimageます。このフィドルを参照してくださいhttp://jsfiddle.net/qGGwx/99/markerに等しい位置で追加しtransform-originます。ご覧のとおり、画像サイズがコンテナー サイズと等しい場合、問題はありません。このスケーリングが必要ですか?たぶん、2番目のコンテナを追加できますか?フィドルでは、条件も追加しましたmousemove

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

これは、ズーム中に画像が移動するのを防ぎますが、問題も引き起こします。が実行中の場合は、ズーム ポイントを変更できませんzoom

于 2013-02-08T15:47:46.133 に答える
3

@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;
        }

    });
于 2013-02-10T19:31:16.307 に答える