Produkte sind für Ihren Onlineshops eines der wichtigtsne Faktoren. Aus diesem Grund haben wir und ein Image-Zoom für das Produktlisting entwickelt, damit der Kunde ohne in die Produktdetails zu klicken eine Vergrößerung des Produktes betrachten kann. Zu sehen ist das unter www.demo-store.de (Bewegen Sie die Maus über ein Produktbild) .

Zur Umsetzung verwenden wir jQuery und mit installierter Extension jQuery-Base.

Wir benötigen die Dateinformationen zum base image, die wir im IMG-Tag mit dem Custom-Attribute zoom unterbringen.
Beispiel:

<img src="image_small.jpg" zoom="image_large.jpg" />

Das zoom-Tag wird ausgelesen und bei einem Mouse-Over kann das größere Bild nachgeladen werden – http://www.mxperts.de/examples/imagezoom/. Die Idee zu diesem Feature liefert mir die jQuery-Klasse miniZoomPan von Gian Carlo Mingati. Allerdings konnte ich seine Umsetzung für unser Zwecke, für Magento, nicht verwenden.


Umsetzung:

1. jQuery:

 /*
 * jQuery miniZoomPan 1.0
 * 2009 Gian Carlo Mingati
 * modified by Johannes Teitge
 * Version: 1.0 (18-JUNE-2009)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Requires:
 * jQuery v1.3.2 or later
 *
 * To define large image use the zoom attribute
 * Example: <img src="images/leaf_s.jpg" zoom="images/leaf_l.jpg"/>
 *
 */
jQuery.fn.listZoom = function(settings) {

	settings = jQuery.extend({
	sW: 10, // small image width
	sH: 10, // small image height
	lW: 20, // large image width
	lH: 20, // large image height
	frameColor: "#cecece",
	frameWidth: 1,
	loaderContent: "loading..." // plain text or an image tag eg.: "<img src='yoursite.com/spinner.gif' />"
	}, settings);

	return this.each(function(){

		var div =  jQuery(this);
		div.css({width: settings.sW, height: settings.sH, border: settings.frameWidth+"px solid "+settings.frameColor}).addClass("listzoom");
		var ig = div.children();

		var igs = jQuery(ig).attr('src');	// small image filename
		var igl = jQuery(ig).attr('zoom'); // large image filename

		ig.css({position: "relative"});
		jQuery(window).bind("load", function() {
  		ig.width(settings.sW);
	  	ig.height(settings.sH);
		});
		div.css({overflow: "hidden"});
		jQuery("<span class='loader'>"+settings.loaderContent+"<\/span>").insertBefore(ig);

		div.mousemove(function(e) {
			var divWidth = div.width();
			var divHeight = div.height();
			var igW = ig.width();
			var igH = ig.height();
			var dOs = div.offset();
			var leftPan = (e.pageX - dOs.left) * (divWidth - igW) / (divWidth+settings.frameWidth*2);
			var topPan = (e.pageY - dOs.top) * (divHeight - igH) / (divHeight+settings.frameWidth*2);
			ig.css({left: leftPan, top: topPan});
		});

		div.hover(
			function () {
				ig.css({ width: settings.lW, height: settings.lH});
				div.children("span.loader").fadeIn(250);
				ig.attr("src",igl).load(function(){ // set large image and fade out after loaded
          div.children("span.loader").fadeOut(250);
        });
			},
			function () {
				ig.css({left: "0", top: "0", width: settings.sW, height: settings.sH});
				ig.attr("src",igs); // set small image;
        div.children("span.loader").fadeOut(250); // hide Loader
			}
		);

	});

};

Die Verwendung im HTML-Code sieht dann wie folgt aus:

<div class="zoom">
	<img src="images/image_small.jpg" zoom="images/image_large.jpg"/>
</div>

<script type= "text/javascript">/*<![CDATA[*/
$(function() {
	$(".zoom").listZoom({
			sW: 400,
			sH: 400,
			lW: 800,
			lH: 800,
			frameColor: "#cecece",
			frameWidth: 1,
			loaderContent: 'Bild wird geladen...'
		})
});
/*]]>*/</script>

Als Parameter werden die Bildgröße der beiden Bilder übergeben, die Farbe und Breite des Bild-Rahmens sowie des Textes für den Bild-Loader. Das Hover-Event ersetzt die Image-Source durch das große Bild und zeigt den “Loading” Text an. Durch Verwendung von load, wir ein nach dem vollständigen laden des Bildes ein Event ausgelöst und der “Loading” Text ausgeblendet.

Mit dem Mousemove Event reagieren wir auf die Mausbwegung und der Benutzer kann sich das Produkt genauer ansehen.

Wir kopieren den Javascript-Code als jquery.listzoom.js in den Ordner
skin/frontend/default/[Ihr Theme]/js.


2. CSS:

.listzoom {
  background: #fff;
  position: relative;
  border: 1px solid lightgray;
  padding: 0;
  margin: 0;
}
.listzoom span.loader {
  position:absolute;
  left:0;
  top:0;
  z-index: 1;
  display: none;
  color: #000;
  background: #fff;
  font: normal 9px Arial, Verdana;
  padding: 3px;
}

Den Javascript-Code kopieren Sie als jquery.listzoom.css in den Ordner
skin/frontend/default/[Ihr Theme]/css.


3. Einbinden über jQuery-Base

Durch die Verwendung von jQuery-Base sparen wir uns das einbinden von css und javascript über XML. Tragen Sie im Backend unter xxx folgendes ein (roter Rahmen).
listzoom_backend



4. Layout

Magento stellt im Produkt-Listing nur eine begrenze Anzahl Datenfelder in der Products-Collcetion zur Verfügung. Über eine Anpassung der catalog.xml, können wir Magento mitteilen, dass wir auch das base image (image) im Produkt-Listing benötigen.

Öffnen Sie dazu die Datei app/design/frontend/default/[Ihr Theme]/layout/catalog.xml und suchen Sie als erstes die Stelle

    <catalog_category_default>
    ...
            <block type="catalog/category_view" name="category.products" template="catalog/category/view.phtml">

    ...

und fügen Sie folgende Zeile ein:

  <action method="addAttribute"><name>image</name></action>

Genauso gehen Sie auch beim layered Navigation Layout vor.

   <catalog_category_layered>
    ...
            <block type="catalog/product_list" name="product_list" template="catalog/product/list.phtml">

    ...

und fügen Sie folgende Zeile ein:

  <action method="addAttribute"><name>image</name></action>

Nun liefert uns die Funktion getLoadedProductCollection() die Daten inklusive dem zusätzlichen Attribut “Image”.


5. Template list.phtml anpassen

Öffnen Sie nun die Datei
app/design/frontend/default/[Ihr Theme]/template/catalog/product/list.phtml

In dieser Template-Datei erfolgt die Ausgabe des Produktlisting als Liste sowie auch als Grid. Beides müssen wir einbeziehen, wobei die Bildgrößen jeweils unterschidlich sein können.

Deshalb platzieren wir jeweils am Ende jeder Schleife über die Products-Collection den jQuery-Code. Die Image-Tags werden um das zoom-Attribute erweitert indem wir das “base image” als Attribut verwenden. Im default Theme sieht das dann in unserem Demo-Store wie folgt aus:

<?php
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Academic Free License (AFL 3.0)
 * that is bundled with this package in the file LICENSE_AFL.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/afl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Magento to newer
 * versions in the future. If you wish to customize Magento for your
 * needs please refer to http://www.magentocommerce.com for more information.
 *
 * @category   design_default
 * @package    Mage
 * @copyright  Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
 * @license    http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)
 */
?>
<?php
/**
 * Product list template
 *
 * @see Mage_Catalog_Block_Product_List
 */
?>
<?php $_productCollection=$this->getLoadedProductCollection() ?>
<?php if(!$_productCollection->count()): ?>
<div class="note-msg">
    <?php echo $this->__('There are no products matching the selection.') ?>
</div>
<?php else: ?>

<?php echo $this->getToolbarHtml() ?>
<?php // List mode ?>
<?php if($this->getMode()!='grid'): ?>
<?php $_iterator = 0; ?>
<div class="listing-type-list catalog-listing">
<?php foreach ($_productCollection as $_product): ?>
    <div class="listing-item<?php if( ++$_iterator == sizeof($_productCollection) ): ?> last<?php endif; ?>">
        <?php // Product Image ?>

        <div class="product-image">

            <a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getSmallImageLabel()) ?>">
               <div class="zoom">
                <img src="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(135, 135); ?>" zoom="<?php echo $this->helper('catalog/image')->init($_product, 'image')->resize(250, 250); ?>" width="135" height="135" alt="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')) ?>" title="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')) ?>" />
               </div>
            </a>
        </div>

        <?php // Product description ?>
        <div class="product-shop">
            <h5><a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getName()) ?>"><?php echo $this->htmlEscape($_product->getName())?></a></h5>
            <?php if($_product->getRatingSummary()): ?>
            <?php echo $this->getReviewsSummaryHtml($_product) ?>
            <?php endif; ?>
            <?php echo $this->getPriceHtml($_product, true) ?>
            <?php if($_product->isSaleable()): ?>
            <button class="form-button" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><?php echo $this->__('Add to Cart') ?></span></button>
            <?php else: ?>
            <div class="out-of-stock"><?php echo $this->__('Out of stock') ?></div>
            <?php endif; ?>
            <div class="clear"></div>
            <div class="description">
                <?php echo nl2br($_product->getShortDescription()) ?>
                <a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getName()) ?>"><small><?php echo $this->__('Learn More') ?></small></a>
            </div>
            <p class="add-to">
                <?php if ($this->helper('wishlist')->isAllow()) : ?>
                <a href="<?php echo $this->helper('wishlist')->getAddUrl($_product) ?>" class="link-cart"><?php echo $this->__('Add to Wishlist') ?></a>
                <?php endif; ?>
                <?php if($_compareUrl=$this->getAddToCompareUrl($_product)): ?>
                <span class="pipe">|</span>
                <a href="<?php echo $_compareUrl ?>"><?php echo $this->__('Add to Compare') ?></a>
                <?php endif; ?>
            </p>
        </div>
    </div>
<?php endforeach; ?>

<script type= "text/javascript">/*<![CDATA[*/
jQuery(function() {
	jQuery(".zoom").listZoom({
			sW: 135,
			sH: 135,
			lW: 250,
			lH: 250,
			loaderContent: 'Bild wird geladen...'
		})
});
/*]]>*/</script>

</div>

<?php else: ?>

<?php // Grid Mode ?>
<div class="listing-type-grid catalog-listing">
<?php $_collectionSize = $_productCollection->count() ?>
    <?php
      $_columnCount = $this->getColumnCount();
 //     $_columnCount = 5;
      ?>
    <?php $i=0; foreach ($_productCollection as $_product): ?>
        <?php if($i++%$_columnCount==0): ?>
        <ol class="grid-row">
        <?php endif; ?>

            <li class="item">
                <p class="product-image">
                    <a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')) ?>">
                        <div class="zoom">
                        <img src="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(80, 80); ?>" zoom="<?php echo $this->helper('catalog/image')->init($_product, 'small_image')->resize(250, 250); ?>" width="80" height="80" alt="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')) ?>" title="<?php echo $this->htmlEscape($this->getImageLabel($_product, 'small_image')) ?>" />
                        </div>
                    </a>
                </p>
                <h5><a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->htmlEscape($_product->getName()) ?>"><?php echo $this->htmlEscape($_product->getName()) ?></a></h5>
<!--
                <?php if($_product->getRatingSummary()): ?>
                <?php echo $this->getReviewsSummaryHtml($_product, 'short') ?>
                <?php endif; ?>
-->
                <?php echo $this->getPriceHtml($_product, true) ?>

                <?php if($_product->isSaleable()): ?>
                    <button type="button" class="form-button" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><?php echo $this->__('Kaufen') ?></span></button>
                <?php else: ?>
                <div class="out-of-stock"><?php echo $this->__('Out of stock') ?></div>
                <?php endif; ?>

                <!--
                <?php if($_product->isSaleable()): ?>
                    <button type="button" class="form-button" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><?php echo $this->__('Add to Cart') ?></span></button>
                <?php else: ?>
                <div class="out-of-stock"><?php echo $this->__('Out of stock') ?></div>
                <?php endif; ?>
                -->
                    <!--
                <p class="add-to">
                    <?php if ($this->helper('wishlist')->isAllow()) : ?>
                    <a href="<?php echo $this->helper('wishlist')->getAddUrl($_product) ?>" class="link-cart"><?php echo $this->__('Add to Wishlist') ?></a>
                    <?php endif; ?>

                    <?php if($_compareUrl=$this->getAddToCompareUrl($_product)): ?><br/>
                    <a href="<?php echo $_compareUrl ?>"><?php echo $this->__('Add to Compare') ?></a>
                    <?php endif; ?>

                </p>
                -->
            </li>
        <?php if($i%$_columnCount==0 || $i==$_collectionSize): ?>
        </ol>
        <?php endif; ?>
    <?php endforeach ?>
    <script type="text/javascript">decorateGeneric($$('.grid-row'), ['last', 'odd', 'even']);</script>

<script type= "text/javascript">/*<![CDATA[*/
jQuery(function() {
	jQuery(".zoom").listZoom({
			sW: 80,
			sH: 80,
			lW: 250,
			lH: 250,
			loaderContent: 'Bild wird geladen...'
		})
});
/*]]>*/</script>

</div>
<?php endif; ?>

<?php echo $this->getToolbarHtml() ?>
<?php endif; ?>

Damit ist auch schon die ganze Arbeit getan und Sie haben im produkt-Listing eine Zoom-Funktion, welche die Textangaben nicht beinflußt und somt die ganzen Produktinfomationen lesbar bleiben.


An dieser Stelle möchten Daniel, Igor und ich uns bei all unseren treuen Lesern bedanken. Danke für die Tipps, Kritik und Anregungen und die vielen E-Mails sowie Kontakten.

Wir hoffen, dass wir im Rahmen unseres Mxperts-Projekts etwas Unterstützung bei der täglichen Arbeit mit Magento geben konnten und vor allem unsere Zielsetzung “Wege aus der Praxis für die Praxis aufzuzeigen” ein Stück weit erreichen konnten.

Wir freuen uns immer wieder über positives Feedback, wenn es unsere Modifikationen / Extensions so wie hier gezeigt, oder auch gerne in komplett anderer Form in Live-Projekte schaffen.

Auch aus euren Ideen ziehen wir neuen & frischen Input! ;-)

Nach diesem Ereignisreichen Jahr gönnen wir uns eine schöpferische Pause und melden uns mitte Januar mit neuen, spannenden Tutorials, Tipps & Extensions zurück.

Wir wünschen allen Lesern ein frohes Weihnachtsfest und einen guten Start ins kommende Jahr 2010.
Johannes Teitge,
Daniel Sasse,
Igor Jankovic