2016-04-03 18 views
13

Live-Beispiel Scrollen: https://jsfiddle.net/b8vLg0ny/Mit CSS-Skala() verwandeln sich in ein Element zu vergrößern, ohne Zuschneiden, Aufrechterhaltung

Es ist möglich, die CSS scale und translate Funktionen zu verwenden, um in Element zu vergrößern.

Nehmen Sie dieses Beispiel von 4 Boxen in einem 2x2 Raster.

HTML:

<div id="container"> 
    <div id="zoom-container"> 
    <div class="box red">A</div> 
    <div class="box blue">B</div> 
    <div class="box green">C</div> 
    <div class="box black">D</div> 
    </div> 
</div> 

CSS:

* { margin: 0; } 

body, html { height: 100%; } 

#container { 
    height: 100%; 
    width: 50%; 
    margin: 0 auto; 
} 

#zoom-container { 
    height: 100%; 
    width: 100%; 
    transition: all 0.2s ease-in-out; 
} 

.box { 
    float: left; 
    width: 50%; 
    height: 50%; 
    color: white; 
    text-align: center; 
    display: block; 
} 

.red { background: red; } 
.blue { background: blue; } 
.green { background: green; } 
.black { background: black; } 

JavaScript:

window.zoomedIn = false; 

$(".box").click(function(event) { 
    var el = this; 
    var zoomContainer = $("#zoom-container"); 

    if (window.zoomedIn) { 
    console.log("resetting zoom"); 
    zoomContainer.css("transform", ""); 
    $("#container").css("overflow", "auto"); 
    window.zoomedIn = false; 
    } else { 
    console.log("applying zoom"); 
    var top = el.offsetTop; 
    var left = el.offsetLeft - 0.25*zoomContainer[0].clientWidth; 

    var translateY = 0.5*zoomContainer[0].clientHeight - top; 
    var translateX = 0.5*zoomContainer[0].clientWidth - left; 

    $("#container").css("overflow", "scroll"); 
    zoomContainer.css("transform", "translate(" + 2 * translateX + "px, " + 2 * translateY + "px) scale(2)"); 
    window.zoomedIn = true; 
    } 
}); 

durch den Wert von translateX Steuern und translateY, können Sie, wie die Zoom Arbeiten ändern.

Die anfängliche gerenderte Ansicht sieht etwa so aus:

initial rendered view

Durch Klicken auf die A-Box finden Sie in geeigneter Weise vergrößern:

zooming to A and then zooming out

(Beachten Sie, dass am Ende klicken D zeigt nur das Zurücksetzen durch Zurückzoomen.)

Das Problem ist: Zoomen auf Box D skaliert den Zoom-Container so, dass das Scrollen nach oben und links nicht funktioniert, da der Inhalt überläuft. Das gleiche passiert beim Zoomen zu den Boxen B (die linke Hälfte ist beschnitten) und C (die obere Hälfte ist beschnitten). Nur bei A wird der Inhalt außerhalb des Containers nicht überlaufen.

In ähnlichen Situationen im Zusammenhang mit der Skalierung (siehe CSS3 Transform Scale and Container with Overflow) ist eine mögliche Lösung die Angabe transform-origin: top left (oder 0 0). Da die Skalierung relativ zur oberen linken Seite funktioniert, bleibt die Bildlauffunktion erhalten. Dies scheint jedoch nicht zu funktionieren, da es bedeutet, dass Sie den Inhalt, der auf das angeklickte Feld (A, B, C oder D) fokussiert werden soll, nicht mehr neu positionieren.

Eine weitere mögliche Lösung besteht darin, dem Zoom-Container einen margin-left und einen margin-top hinzuzufügen, der genügend Platz bietet, um den übergelaufenen Inhalt auszugleichen. Aber nochmal: die translate Werte stehen nicht mehr aneinander.

Also: Gibt es einen Weg, um beide Zoom auf einem bestimmten Element, und Überlauf mit einer Rolle, so dass Inhalte nicht beschnitten?

aktualisieren: Es gibt eine grobe fast-Lösung von scrollTop und scrollLeft, ähnlich wie https://stackoverflow.com/a/31406704/528044 (siehe the jsfiddle example) Animieren, aber es ist nicht ganz die richtige Lösung, weil es zuerst nach links oben zoomt, nicht das beabsichtigte Ziel. Ich fange an zu vermuten, dass dies eigentlich nicht möglich ist, weil es wahrscheinlich äquivalent ist, wenn man nach scrollLeft fragt, um negativ zu sein.

+0

Wenn ich sage, alle Kästen wird vergrößert aber die eine geklickt sollte im Hinblick sein, nicht wahr ?, und dann, durch Scrollen kann man sich bewegen Irgendwelche Boxen, oder? – LGSon

+0

Klicken Sie auf ein Kästchen, um das angeklickte Feld zu vergrößern. Es sollte dann möglich sein, zu einem der anderen Felder zu scrollen, um sie in Sicht zu bringen. –

+1

Ich arbeite Stück für Stück mit diesem, habe so weit, https://jsfiddle.net/LGSon/b8vLg0ny/5/ ... und ich könnte ein Kopfgeld anbieten, wenn möglich (2 Tage nach der Entsendung), zu geben mehr Attraktivität für Ihre Frage, wie ich denke, es ist ein guter, und ich habe auch ein Interesse an einer ähnlichen Lösung – LGSon

Antwort

4

Warum nicht einfach die TransformOrigin-0 0 neu zu positionieren und die richtigen zu scrollTop/scrollLeftnach die Animation zu verwenden?

Wenn Sie die Animation nicht benötigen, die TransformOrigin kann immer bleibt 0 0 und nur das Scrollen verwendet wird, um das Feld zu zeigen, .

Um die Animation weniger sprunghaft zu machen, verwenden Sie den Übergang nur für transform propertey, sonst wird auch die transform-origin animiert. Ich habe das Beispiel mit 4x4 Elementen bearbeitet, aber ich finde es sinnvoll, eine Box komplett in Sicht zu zoomen, deshalb habe ich die Zoomstufe geändert. Bleibt man aber beispielsweise bei Zoomstufe 2 und der Rastergröße 15x15, dann sollte bei dieser Vorgehensweise wirklich genaue Herkunft für die Transformation und dann auch das korrekte Scrollen berechnet werden.

Wie auch immer, ich weiß nicht, ob Sie diesen Ansatz nützlich finden.

Stapel Snippet

var zoomedIn = false; 
 
var zoomContainer = $("#zoom-container"); 
 

 
$(".box").click(function(event) { 
 
    var el = this; 
 
    
 
    if (zoomedIn) {  
 
    zoomContainer.css({ 
 
    \t transform: "scale(1)", 
 
     transformOrigin: "0 0" 
 
    }); 
 
    zoomContainer.parent().scrollTop(0).scrollLeft(0); 
 
    zoomedIn = false; 
 
    return; 
 
    } 
 
    zoomedIn = true; 
 
    var $el = $(el); 
 
    animate($el); 
 
    zoomContainer.on('transitionend', function(){ 
 
    \t zoomContainer.off('transitionend'); 
 
    \t reposition($el); 
 
    }) 
 
}); 
 

 
var COLS = 4, ROWS = 4, 
 
    \t COLS_STEP = 100/(COLS - 1), ROWS_STEP = 100/(ROWS - 1), 
 
    ZOOM = 4; 
 
    
 

 
function animate($box) { 
 
    var cell = getCell($box); 
 
    var col = cell.col * COLS_STEP + '%', 
 
     row = cell.row * ROWS_STEP + '%'; 
 
    zoomContainer.parent().css('overflow', 'hidden'); 
 
\t zoomContainer.css({ 
 
    transition: 'transform 0.2s ease-in-out', 
 
    \t transform: "scale(" + ZOOM + ")", 
 
    transformOrigin: col + " " + row 
 
    }); 
 
} 
 
function reposition($box) { 
 
    zoomContainer.css({ 
 
    transition: 'none', 
 
    \t transform: "scale(" + ZOOM + ")", 
 
    transformOrigin: '0 0' 
 
    }); 
 
    zoomContainer.parent().css('overflow', 'auto'); 
 
    $box.get(0).scrollIntoView(); 
 
} 
 
function getCell ($box) { 
 
\t var idx = $box.index(); 
 
    var col = idx % COLS, 
 
     row = (idx/ROWS) | 0; 
 
    return { col: col, row: row }; 
 
}
* { margin: 0; } 
 

 
body, html { height: 100%; } 
 

 
#container { 
 
    height: 100%; 
 
    width: 50%; 
 
    margin: 0 auto; 
 
    overflow: hidden; 
 
} 
 

 
#zoom-container { 
 
    height: 100%; 
 
    width: 100%; 
 
    will-change: transform; 
 
} 
 

 
.box { 
 
    float: left; 
 
    width: 25%; 
 
    height: 25%; 
 
    color: white; 
 
    text-align: center; 
 
} 
 

 
.red { background: red; } 
 
.blue { background: blue; } 
 
.green { background: green; } 
 
.black { background: black; } 
 
.l { opacity: .3 }
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script> 
 

 
<div id="container"> 
 
    <div id="zoom-container"> 
 
    <div class="box red">A</div> 
 
    <div class="box blue">B</div> 
 
    <div class="box green">C</div> 
 
    <div class="box black">D</div> 
 

 
    <div class="box red l">E</div> 
 
    <div class="box blue l">F</div> 
 
    <div class="box green l">G</div> 
 
    <div class="box black l">H</div> 
 

 
    <div class="box red">I</div> 
 
    <div class="box blue">J</div> 
 
    <div class="box green">K</div> 
 
    <div class="box black">L</div> 
 

 
    <div class="box red l">M</div> 
 
    <div class="box blue l">N</div> 
 
    <div class="box green l">O</div> 
 
    <div class="box black l">P</div> 
 
    </div> 
 
</div>

+0

Meistens weil, wann immer ich das ausprobiert habe, scheint es für große Grids (zB 10x10 oder 15x15) kaputt zu gehen. Zum Beispiel scheint mir das ziemlich nervös zu sein, wenn ich im unteren rechten Bereich auf die Kästchen klicke: https://jsfiddle.net/b8vLg0ny/9/ (obwohl ich zugeben muss, dass mir etwas im JS fehlen könnte, wo ich nicht genügend Werte aktualisiert habe). Die Animation, die auf "0 0" zoomt, endet nicht synchron mit der Bildlaufanimation. –

+0

@AdamPrescott Nach _tenbits_ update, funktioniert es jetzt so, wie Sie möchten? ... Fragen, weil ich keinen Sinn darin sehe, meine weiter zu verbessern, da dies einmal großartig ist. – LGSon

+0

Das sieht nach der bestmöglichen Lösung aus! –

1

aktualisieren

ich auf Rollbalken stecken blieb nicht die ganze Zeit zeigt, so brauche ich diesen Teil zu untersuchen, so dass Code wird kommentiert und stattdessen verwende ich eine Verzögerung, die angeklickt Feld in den Blick zu bewegen.

Hier ist my fiddle demo, mit dem ich spiele, um herauszufinden, wie das Problem mit der Bildlaufleiste gelöst werden kann.

Seitlicher Hinweis: In einem Kommentar von @AVAVT möchte ich auf his post here verlinken, da das vielleicht jemand anderen helfen könnte, was ich in einigen Fällen als interessante Alternative empfinde.

(function(zoomed) { 
 
    
 
    $(".box").click(function(event) { 
 
    
 
    var el = this, elp = el.parentElement; 
 
    
 
    if (zoomed) { 
 
     zoomed = false; 
 
     $("#zoom-container").css({'transform': ''}); 
 
     
 
    } else { 
 
     zoomed = true; 
 
     /* this zooms correct but show 1 or none scroll for B,C,D so need to figure out why 
 
     
 
     var tro = (Math.abs(elp.offsetTop - el.offsetTop) > 0) ? 'bottom' : 'top'; 
 
     tro += (Math.abs(elp.offsetLeft - el.offsetLeft) > 0) ? ' right' : ' left'; 
 
     $("#zoom-container").css({'transform-origin': tro, 'transform': 'scale(2)'}); 
 
     */ 
 
     
 
     $("#zoom-container").css({'transform-origin': '0 0', 'transform': 'scale(2)'}); 
 
     /* delay needed before scroll into view */  
 
     setTimeout(function() { 
 
     el.scrollIntoView(); 
 
     },250); 
 
    }  
 
    }); 
 
})();
* { margin: 0; } 
 

 
body, html { height: 100%; } 
 

 
#container { 
 
    height: 100%; 
 
    width: 50%; 
 
    overflow: auto; 
 
    margin: 0 auto; 
 
} 
 

 
#zoom-container { 
 
    height: 100%; 
 
    width: 100%; 
 
    transition: all 0.2s ease-in-out; 
 
} 
 

 
.box { 
 
    float: left; 
 
    width: 50%; 
 
    height: 50%; 
 
    color: white; 
 
    text-align: center; 
 
    display: block; 
 
} 
 

 
.red { 
 
    background: red; 
 
} 
 
.blue { 
 
    background: blue; 
 
} 
 
.green { 
 
    background: green; 
 
} 
 
.black { 
 
    background: black; 
 
}
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script> 
 
<div id="container"> 
 
    <div id="zoom-container"> 
 
    <div class="box red">A</div> 
 
    <div class="box blue">B</div> 
 
    <div class="box green">C</div> 
 
    <div class="box black">D</div> 
 
    </div> 
 
</div>

+0

Dies funktioniert nicht ganz. Vergleichen Sie, indem Sie in meinem ursprünglichen Beispiel auf D klicken und in diesem auf D klicken. Sie sind nicht richtig ausgerichtet, daher sind die Positionen ausgeschaltet. Ähnlich mit B: Es ist um die halbe Breite der Ansicht verschoben. –

+1

@AdamPrescott Ja, Sie müssen das anpassen, ich habe nicht, nur gezeigt, wie Sie beide Bildlaufleisten und Zoom erhalten. – LGSon

+0

Sicher. Es zoomt, aber auf dem falschen Teil, was hier das Hauptproblem ist. –

1

ich meine eigene Frage zu beantworten, da ich ziemlich sicher bin, dass es mit den gegebenen Anforderungen tatsächlich nicht möglich ist. Zumindest nicht ohne irgendeinen Hacker, der visuell Probleme verursachen würde, z. B. durch jumpiges Scrollen, indem scrollTop nach dem Wechseln von transform-origin zu 0, 0 animiert wird (wodurch der Ausschnitt entfernt wird, indem alles in den Container zurückgebracht wird).

Ich würde gerne für jemanden mich zu beweisen falsch, aber es scheint gleichbedeutend mit der Frage scrollLeft = -10, etwas, das MDN will tell you is not possible. ("Wenn auf einen Wert kleiner als 0 [...] eingestellt ist, ist scrollLeft auf 0 gesetzt.")

Wenn es jedoch akzeptabel ist, die Benutzeroberfläche vom Scrollen, Zoomen und Ziehen/Verschieben zu ändern, es ist dann erreichbar: https://jsfiddle.net/jegn4x0f/5/

Hier ist die Lösung mit dem gleichen Kontext wie mein ursprüngliches Problem:

zoom-pan

HTML:

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script> 

<button id="zoom-out">Zoom out</button> 

<div id="container"> 
    <div id="inner-container"> 
    <div id="zoom-container"> 
     <div class="box red">A</div> 
     <div class="box blue">B</div> 
     <div class="box green">C</div> 
     <div class="box black">D</div> 
    </div> 
    </div> 
</div> 

JavaScript:

// 
// credit for the approach goes to 
// 
// https://stackoverflow.com/questions/35252249/move-drag-pan-and-zoom-object-image-or-div-in-pure-js#comment58224460_35253567 
// 
// and the corresponding example: 
// 
// https://jsfiddle.net/j8kLz6wm/1/ 
// 

// in a real-world setting, you 
// wouldn't keep this information 
// on window. this is just for 
// the demonstration. 
window.zoomedIn = false; 

// stores the initial translate values after clicking on a box 
window.translateY = null; 
window.translateX = null; 

// stores the incremental translate values based on 
// applying the initial translate values + delta 
window.lastTranslateY = null; 
window.lastTranslateX = null; 

// cursor position relative to the container, at 
// the time the drag started 
window.dragStartX = null; 
window.dragStartY = null; 

var handleDragStart = function(element, xCursor, yCursor) { 
    window.dragStartX = xCursor - element.offsetLeft; 
    window.dragStartY = yCursor - element.offsetTop; 

    // disable transition animations, since we're starting a drag 
    $("#zoom-container").css("transition", "none"); 
}; 

var handleDragEnd = function() { 
    window.dragStartX = null; 
    window.dragStartY = null; 
    // remove the individual element's styling for transitions 
    // which brings back the stylesheet's default of animating. 
    $("#zoom-container").css("transition", ""); 

    // keep track of the translate values we arrived at 
    window.translateY = window.lastTranslateY; 
    window.translateX = window.lastTranslateX; 
}; 

var handleDragMove = function(xCursor, yCursor) { 
    var deltaX = xCursor - window.dragStartX; 
    var deltaY = yCursor - window.dragStartY; 

    var translateY = window.translateY + (deltaY/2); 
    // the subtracted value here is to keep the letter in the center 
    var translateX = window.translateX + (deltaX/2) - (0.25 * $("#inner-container")[0].clientWidth); 

    // fudge factor, probably because of percentage 
    // width/height problems. couldn't really trace down 
    // the underlying cause. hopefully the general approach 
    // is clear, though. 
    translateY -= 9; 
    translateX -= 4; 

    var innerContainer = $("#inner-container")[0]; 

    // cap all values to prevent infinity scrolling off the page 
    if (translateY > 0.5 * innerContainer.clientHeight) { 
    translateY = 0.5 * innerContainer.clientHeight; 
    } 

    if (translateX > 0.5 * innerContainer.clientWidth) { 
    translateX = 0.5 * innerContainer.clientWidth; 
    } 

    if (translateY < -0.5 * innerContainer.clientHeight) { 
    translateY = -0.5 * innerContainer.clientHeight; 
    } 

    if (translateX < -0.5 * innerContainer.clientWidth) { 
    translateX = -0.5 * innerContainer.clientWidth; 
    } 

    // update the zoom container's translate values 
    // based on the original + delta, capped to the 
    // container's width and height. 
    $("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)"); 

    // keep track of the updated values for the next 
    // touchmove event. 
    window.lastTranslateX = translateX; 
    window.lastTranslateY = translateY; 
}; 

// Drag start -- touch version 
$("#container").on("touchstart", function(event) { 
    if (!window.zoomedIn) { 
    return true; 
    } 

    var xCursor = event.originalEvent.changedTouches[0].clientX; 
    var yCursor = event.originalEvent.changedTouches[0].clientY; 

    handleDragStart(this, xCursor, yCursor); 
}); 

// Drag start -- mouse version 
$("#container").on("mousedown", function(event) { 
    if (!window.zoomedIn) { 
    return true; 
    } 

    var xCursor = event.clientX; 
    var yCursor = event.clientY; 

    handleDragStart(this, xCursor, yCursor); 
}); 

// Drag end -- touch version 
$("#inner-container").on("touchend", function(event) { 
    if (!window.zoomedIn) { 
    return true; 
    } 

    handleDragEnd(); 
}); 

// Drag end -- mouse version 
$("#inner-container").on("mouseup", function(event) { 
    if (!window.zoomedIn) { 
    return true; 
    } 

    handleDragEnd(); 
}); 

// Drag move -- touch version 
$("#inner-container").on("touchmove", function(event) { 
    // prevent pull-to-refresh. could be smarter by checking 
    // if the page's scroll y-offset is 0, and even smarter 
    // by checking if we're pulling down, not up. 
    event.preventDefault(); 

    if (!window.zoomedIn) { 
    return true; 
    } 

    var xCursor = event.originalEvent.changedTouches[0].clientX; 
    var yCursor = event.originalEvent.changedTouches[0].clientY; 

    handleDragMove(xCursor, yCursor); 
}); 

// Drag move -- click version 
$("#inner-container").on("mousemove", function(event) { 
    // prevent pull-to-refresh. could be smarter by checking 
    // if the page's scroll y-offset is 0, and even smarter 
    // by checking if we're pulling down, not up. 
    event.preventDefault(); 

    // if we aren't dragging from anywhere, don't move 
    if (!window.zoomedIn || !window.dragStartX) { 
    return true; 
    } 

    var xCursor = event.clientX; 
    var yCursor = event.clientY; 

    handleDragMove(xCursor, yCursor); 
}); 

var zoomInTo = function(element) { 
    console.log("applying zoom"); 

    var top = element.offsetTop; 
    // the subtracted value here is to keep the letter in the center 
    var left = element.offsetLeft - (0.25 * $("#inner-container")[0].clientWidth); 

    var translateY = 0.5 * $("#zoom-container")[0].clientHeight - top; 
    var translateX = 0.5 * $("#zoom-container")[0].clientWidth - left; 

    $("#container").css("overflow", "scroll"); 
    $("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)"); 
    window.translateY = translateY; 
    window.translateX = translateX; 

    window.zoomedIn = true; 
} 

var zoomOut = function() { 
    console.log("resetting zoom"); 

    window.zoomedIn = false; 
    $("#zoom-container").css("transform", ""); 
    $("#zoom-container").css("transition", ""); 
    window.dragStartX = null; 
    window.dragStartY = null; 
    window.dragMoveJustHappened = null; 
    window.translateY = window.lastTranslateY; 
    window.translateX = window.lastTranslateX; 
    window.lastTranslateX = null; 
    window.lastTranslateY = null; 
} 

$(".box").click(function(event) { 
    var element = this; 
    var zoomContainer = $("#zoom-container"); 

    if (!window.zoomedIn) { 
    zoomInTo(element); 
    } 
}); 

$("#zoom-out").click(function(event) { 
    zoomOut(); 
}); 

CSS:

* { 
    margin: 0; 
} 

body, 
html { 
    height: 100%; 
} 

#container { 
    height: 100%; 
    width: 50%; 
    margin: 0 auto; 
} 

#inner-container { 
    width: 100%; 
    height: 100%; 
} 

#zoom-container { 
    height: 100%; 
    width: 100%; 
    transition: transform 0.2s ease-in-out; 
} 

.box { 
    float: left; 
    width: 50%; 
    height: 50%; 
    color: white; 
    text-align: center; 
    display: block; 
} 

.red { 
    background: red; 
} 

.blue { 
    background: blue; 
} 

.green { 
    background: green; 
} 

.black { 
    background: black; 
} 

ich diese zusammen aus einer anderen Frage pieced (Move (drag/pan) and zoom object (image or div) in pure js), wo die width und height geändert werden. Das trifft in meinem Fall nicht ganz zu, da ich auf ein bestimmtes Element auf der Seite zoomen muss (mit vielen Boxen als in einem 2x2-Raster). Die Lösung dieser Frage (https://jsfiddle.net/j8kLz6wm/1/) zeigt den grundlegenden Ansatz in reinem JavaScript. Wenn Sie jQuery verfügbar haben, können Sie wahrscheinlich einfach jquery.panzoom verwenden.