2011-12-30 6 views
9

Sembra che dovrebbe essere abbastanza semplice, ma per qualche motivo non riesco a circondarmi del tutto. Ho un'immagine all'interno di un div "viewport", di cui la proprietà overflow è impostata su hidden.Javascript (jQuery) Ridimensiona un'immagine al punto centrale del contenitore

Ho implementato un semplice zoom e panoramica con l'interfaccia utente jQuery, tuttavia ho problemi a far sì che lo zoom appaia dal centro della finestra. Ho fatto un piccolo screencast da Photoshop all'effetto che sto cercando di riprodurre: http://dl.dropbox.com/u/107346/share/reference-point-zoom.mov

In PS è possibile regolare il punto di riferimento per il ridimensionamento e l'oggetto verrà scalato da quel punto. Ovviamente questo non è possibile con HTML/CSS/JS, quindi sto cercando di trovare i valori CSS di sinistra e top appropriati per simulare l'effetto.

Ecco il codice in questione, con un paio di pezzi inutili rimossi:

html

<div id="viewport"> 
    <img id="map" src="http://dl.dropbox.com/u/107346/share/fake-map.png" alt="" /> 
</div> 

<div id="zoom-control"></div> 

javascript

$('#zoom-control').slider({ 
    min: 300, 
    max: 1020, 
    value: 300, 
    step: 24, 
    slide: function(event, ui) { 
     var old_width = $('#map').width(); 
     var new_width = ui.value; 
     var width_change = new_width - old_width; 
     $('#map').css({ 
      width: new_width, 

      // this is where I'm stuck... 
      // dividing by 2 makes the map zoom 
      // from the center, but if I've panned 
      // the map to a different location I'd 
      // like that reference point to change. 
      // So instead of zooming relative to 
      // the map image center point, it would 
      // appear to zoom relative to the center 
      // of the viewport. 
      left: "-=" + (width_change/2), 
      top: "-=" + (width_change/2) 
     }); 
    } 
}); 

Ecco il progetto JSFiddle: http://jsfiddle.net/christiannaths/W4seR/

risposta

1

Ho sempre fatto affidamento su the kindness of strangers. modifiche pertinenti:

// Calculate the offset as a percentage, accounting for the height of the window 
var x_offset = ((map.position().left-150))/(old_width/2); 
var y_offset = ((map.position().top-150))/(old_width/2); 

var css_properties = { 
    width: new_width, 
    // Set the offset based on the existing percentage rather than 1/2 
    // then readjust for the height of the window 
    left: (new_width * x_offset /2) + 150 + "px", 
    top: (new_width * y_offset /2) + 150 + "px" 
}; 

Riposizionare l'hardcoded 150 con un set variabile viewport di un'istanza, se necessario.

+0

Brillante! Grazie :) – Christian

0

Ecco una versione di lavoro rapido: http://jsfiddle.net/flabbyrabbit/chLkZ/

Probabilmente non è la soluzione più pulita, ma sembra funzionare bene, spero che aiuta.

Aggiornamento: scusa funziona solo se lo zoom è 0 quando la mappa viene spostata.

+0

Hmm, no, non sembra funzionare, se lo desideri, la padella iniziale e il primo zoom dopo i lavori, ma poi le cose iniziano ad andare male dopo. Inoltre, l'immagine del "mirino" è solo lì per fare più facilmente riferimento al punto centrale del div "viewport", nella versione finale sarebbe completamente rimosso, quindi non può essere utilizzato da nessuna parte nei calcoli. – Christian

5

Ecco la soluzione di lavoro. Spiegherò la logica alla prossima modifica.

Funzione logica:

  • Sommario: Ricordate la posizione di centro dell'immagine, relativamente.
    I calcoli per larghezza e altezza sono simili, spiegherò solo il calcolo height
    La spiegazione dettagliata è solo un esempio di logica di funzionamento. Il codice reale, con diversi nomi di variabili, si trova nella parte inferiore della risposta.

    1. Calcolare il centro (x, y) del #map, rispetto al #viewport. Questo può essere fatto usando i metodi offset(), height() e width().

      // Absolute difference between the top border of #map and #viewport 
      var differenceY = viewport.offset().top - map.offset().top; 
      // We want to get the center position, so add it. 
      var centerPosition = differenceY + viewport.height() * 0.5; 
      // Don't forget about the border (3px per CSS) 
      centerPosition += 3; 
      // Calculate the relative center position of #map 
      var relativeCenterY = centerPosition/map.height(); 
      // RESULT: A relative offset. When initialized, the center of #map is at 
      // the center of #viewport, so 50% (= 0.5) 
      // Same method for relativeCenterX 
      
    2. calcolare il nuovo superiore e sinistro offset:

      // Calculate the effect of zooming (example: zoom 1->2 = 2) 
      var relativeChange = new_width/old_width; 
      // Calculate the new height 
      var new_height = relativeChange * old_height; 
      // Calculate the `top` and `left` CSS properties. 
      // These must be negative if the upperleft corner is outside he viewport 
      // Add 50% of the #viewport's height to correctly position #map 
      // (otherwise, the center will be at the upperleft corner) 
      var newTopCss = -relativeCenterY * new_height + 0.5 * viewport.height(); 
      
    3. modificare la proprietà CSS

      map.css("top", newTopCss); 
      

Demo: http://jsfiddle.net/W4seR/12/

var map = $('#map'); 
var viewport = $('#viewport'); 
// Cache the size of the viewport (300x300) 
var viewport_size = { 
    x: viewport.width(), 
    y: viewport.height() 
}; 

map.draggable(); 

$('#zoom-control').slider({ 
    min: 300, 
    max: 1020, 
    value: 300, 
    step: 24, 
    create: function() { 
     map.css({ 
      'width': 300, 
      'left': 0, 
      'top': 0 
     }); 
    }, 
    slide: function(event, ui) { 
     var old_width = map.width(); 
     var old_height = map.height(); 
     var viewport_offset = viewport.offset(); 
     var offset = map.offset(); 
     offset = { 
      top: viewport_offset.top - offset.top + .5*viewport_size.y +3, 
      left: viewport_offset.left - offset.left + .5*viewport_size.x +3 
     }; 
     // Relative offsets, relative to the center! 
     offset.top = offset.top/old_height; 
     offset.left = offset.left/old_width; 

     var new_width = ui.value; 
     var relative = new_width/old_width; 
     var new_height = relative * old_height; 

     offset = { 
      top: -offset.top * new_height + .5*viewport_size.y, 
      left: -offset.left * new_width + .5*viewport_size.x 
     }; 

     var css_properties = { 
      width: new_width, 
      left: offset.left, 
      top: offset.top 
     }; 

     map.css(css_properties); 

     trace((map.position().left)); 
    } 
}); 
+0

+1 per una soluzione di lavoro, accettata la risposta da @RSG anche se è arrivata prima. Grazie mille :) – Christian

+0

@Christian Ho aggiunto una dettagliata spiegazione della logica, non solo del codice. –

+0

Grazie mille, la spiegazione è molto apprezzata. Sicuramente mi aiuta ad avvolgere il mio cervello intorno ad esso :) – Christian