HTML缩放到鼠标位置

时间:2016-04-27 04:04:02

标签: javascript html zoom

我希望能够使用鼠标滚轮放大/缩小HTML页面。我已经包含了我现在所拥有的一个快速的最小工作示例。如果有人能告诉我如何使用这个功能,我将不胜感激。

如果有人想知道,我不使用HTML Canvas,因为虽然我几年前使用它,但我发现当涉及到文本时,标准HTML的表现要好得多。

另外,我从网上得到了这个小例子,但我找不到链接。我并不认为这是原创作品。

编辑: here是在HTML Canvas上放大/缩小鼠标位置的示例(来自SO答案{{3}})。我想模拟这个相同的功能,但对于我的HTML页面 MWE:index.html

<html>
<head>
<title>Sample "Unicron" Application</title>
<style>
/* Prevent the text contents of draggable elements from being selectable. */
figure img { border: 1px solid #ccc; }
h1,h2,h3,h4 { clear: both; }
/* Prevent the contents of draggable elements from being selectable. */
[draggable] {
  -moz-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  user-select: none;
  /* Required to make elements draggable in old WebKit */
  -khtml-user-drag: element;
  -webkit-user-drag: element;
}
dd {
  padding: 5px 0;
}
.column {
  height: 150px;
  width: 150px;
  float: left;
  border: 2px solid #666666;
  background-color: #ccc;
  margin-right: 5px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -o-border-radius: 10px;
  -ms-border-radius: 10px;
  border-radius: 10px;
  -webkit-box-shadow: inset 0 0 3px #000;
  -moz-box-shadow: inset 0 0 3px #000;
  -ms-box-shadow: inset 0 0 3px #000;
  -o-box-shadow: inset 0 0 3px #000;
  box-shadow: inset 0 0 3px #000;
  text-align: left;
  cursor: move;
  margin-bottom: 30px;
}
.column header {
  color: #fff;
  text-shadow: #000 0 1px;
  box-shadow: 5px;
  padding: 5px;
  background: -moz-linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
  background: -webkit-gradient(linear, left top, right top,
                               color-stop(0, rgb(0,0,0)),
                               color-stop(0.50, rgb(79,79,79)),
                               color-stop(1, rgb(21,21,21)));
  background: -webkit-linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
  background: -ms-linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
  background: -o-linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
  border-bottom: 1px solid #ddd;
  -webkit-border-top-left-radius: 10px;
  -moz-border-radius-topleft: 10px;
  -ms-border-radius-topleft: 10px;
  -o-border-radius-topleft: 10px;
  border-top-left-radius: 10px;
  -webkit-border-top-right-radius: 10px;
  -moz-border-radius-topright: 10px;
  -ms-border-radius-topright: 10px;
  -o-border-radius-topright: 10px;
  border-top-right-radius: 10px;
  text-align: center;
}

.column p {
   padding-left: 10px;
   padding-right: 10px;
   padding-top: 0px;
   padding-bottom: 0px;
}

#columns-full .column {
  -webkit-transition: -webkit-transform 0.2s ease-out;
  -moz-transition: -moz-transform 0.2s ease-out;
  -o-transition: -o-transform 0.2s ease-out;
  -ms-transition: -ms-transform 0.2s ease-out;
}
#columns-full .column.over,
#columns-dragOver .column.over,
#columns-dragEnd .column.over,
#columns-almostFinal .column.over {
  border: 2px dashed #000;
}
#columns-full .column.moving {
  opacity: 0.25;
  -webkit-transform: scale(0.8);
  -moz-transform: scale(0.8);
  -ms-transform: scale(0.8);
  -o-transform: scale(0.8);
}
#columns-full .column .count {
  padding-top: 15px;
  font-weight: bold;
  text-shadow: #fff 0 1px;
}

.column.over {
   border: 2px dashed #000;
}

.column:hover {
   transform: scale(2.0);
   z-index: 9999;
}
</style>
</head>
<body bgcolor=white id="body">

<div id="columns-full">
</div>

<script src="nodes.js" type="text/javascript"/></script>
<script src="general.js" type="text/javascript"/></script>
</body>
</html>

MWE:node.js

var browserHeight = document.documentElement.clientHeight;
var browserWidth = document.documentElement.clientWidth;
var nodeHeight = 150;
var nodeWidth = 150;
var nodePad = nodeHeight/10;
var typeCounter = 0;
var type = {
   conversation : typeCounter++,
   discussion : typeCounter++,
   duration : typeCounter++,
   eMail : typeCounter++,
   food : typeCounter++,
   issue : typeCounter++,
   mainTopic : typeCounter++,
   meeting : typeCounter++,
   phoneCall : typeCounter++,
   time : typeCounter++,
};

/*
 * An indivisible task object
 * Has all attributes
 * Has HTML element(s)
 */
class Node {
   constructor (name, body, type )
   {
      this.name = name;
      this.body = body;
      this.type = type;
      this.x = null;
      this.y = null;
      this.div = document.createElement ("div");
      this.div.className = "column";
      this.div.setAttribute ("draggable", "true");
      this.div.style.position="absolute";
      this.initializeInnerHTML (body);
      this.children = [];
      this.requiredWidth = null;
      this.leftBound = null; //space for children
      this.topBound = null; //space for children
      this.numDescendantsInc = null;
      this.numDescendantsExc = null;
      this.boundRow = null;   //row in bounding box
      this.boundCol = null;   //col in bounding box
      this.boundNodeCount = null;
      this.rowWidth = [];  //width of every row
      this.widestRow = null;  //widest row of its children
   }

   /*Create the Div element, adds the name as a 
    * header and adds any text it may have as a
    * body.
    * Must be called after initializeForest because 
    * one needs to know how many child elements 
    * there are for positioning purposes.
    */
   initializeInnerHTML ( body )
   {
      var i;

      this.div.innerHTML = "<header>" + this.name + "</header>"

      for (i=0; i< body.length; i++)
      {
         this.div.innerHTML += "<p>" + this.body + "</p>";
         if ( body.length > 1 && i < body.length - 1 )
         {
            this.div.innerHTML += "<br>";
         }
      }
   }

   /*
    * Calculate the bounding widths required by all
    * Nodes.
    * Runs a depth first test
    */
   updateNumDescendants ()
   {
      var i;

      this.numDescendantsInc = 0;
      this.numDescendantsExc = 0;

      for (i=0; i<this.children.length;i++)
      {
         this.numDescendantsExc += this.children [i].updateNumDescendants ();
      }

      this.numDescendantsInc = this.numDescendantsExc + 1;
      return this.numDescendantsInc;
   }

   /*
    * Goes through and assins top and right bounds.
    * These can then be used to render the nodes
    * using only their local information.
    */
   updateBounds ( leftBound, topBound )
   {
      var i;
      var childLeftBound;
      var childTopBound;
      var count;
      var lastLeft;

      this.leftBound = leftBound || 0;
      this.topBound = topBound || 0;

      lastLeft = this.leftBound;

      for (i=0; i<this.children.length;i++)
      {
         count = this.children [i].updateBounds ( lastLeft, this.topBound + nodeHeight + 2*nodePad )

         lastLeft += (count+1) * (nodeWidth + 2*nodePad );
         //console.log(lastLeft+"+="+count+"*("+nodeWidth+"2*"+nodePad+")");
      }

      if ( this.numDescendantsInc %2 == 0 )
      {
         this.boundNodeCount = this.numDescendantsInc;
      }
      else
      {
         this.boundNodeCount = this.numDescendantsExc;
      }
      return this.boundNodeCount;
   }

   /*
    * Goes through all the bounds and determines the
    * node position within those bounds
   updateIndeces ( boundRow )
   {
      var i;

      this.boundRow = boundRow || 0;

      for (i=0; i<this.children.length;i++)
      {
         this.children [i].updateIndeces ( this.boundRow + 1 )
      }

      if ( this.numDescendantsInc %2 == 0 )
      {
         //sum all nodes to left
         //add one to it
      }
      else
      {
         //sum all nodes to left
         //Find the halfway point of middle child
      }
   }
    */

   /*
    * Goes through all indeces and converts to positions
    */
   updatePositions ()
   {
      var i;

      for (i=0; i<this.children.length;i++)
      {
         this.children [i].updatePositions ()
      }
      this.boundCol = this.numDescendantsInc/2;

      this.x = this.leftBound + this.boundCol * (nodeWidth+2*nodePad);
      this.y = this.topBound + this.boundRow * (nodeHeight+2*nodePad);

      this.div.style.left = this.x;
      this.div.style.top = this.y;
   }

   appendChild ( child )
   {
      this.children [this.children.length] = child;
   }

   /*
    * Must only be called from root node
    */
   updateAll ()
   {
      this.updateNumDescendants();
      this.updateBounds();
      this.updatePositions();
   }

   /*
    * Must only be called from root node
    */
   display ( parentDiv )
   {
      var i;

      parentDiv.appendChild ( this.div );
      for (i=0; i<this.children.length;i++)
      {
         this.children [i].display ( parentDiv )
      }
   }
}

var parentDiv;
var node;
var child1, child2;

parentDiv = document.getElementById("columns-full");

node = new Node ( "Something",["A","B"], type.mainTopic );
node.appendChild ( new Node ( "Another Thing", [""], type.date ) );
node.appendChild ( new Node ( "Hello", [""], type.issue ) );
node.appendChild ( new Node ( "Bye", [""], type.date ) );
node.appendChild ( new Node ( "you", [""], type.time ) );

child1 =  new Node ( "me", [""], type.time ) 
child2 =  new Node ( "them", [""], type.time ) 

child1.appendChild (child2);
node.appendChild (child1);

node.updateAll ();
node.display ( parentDiv );
console.log(node);

MWE:general.js

/*if (Modernizr.draganddrop)
{
   // Browser supports HTML5 DnD.
} else
{
   // Fallback to a library solution.
}
*/

// Using this polyfill for safety.
Element.prototype.hasClassName = function(name) {
  return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(this.className);
};

Element.prototype.addClassName = function(name) {
  if (!this.hasClassName(name)) {
    this.className = this.className ? [this.className, name].join(' ') : name;
  }
};

Element.prototype.removeClassName = function(name) {
  if (this.hasClassName(name)) {
    var c = this.className;
    this.className = c.replace(new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), "");
  }
};


var samples = samples || {};

// dragStart
(function() {
  var id_ = 'columns-dragStart';
  var cols_ = document.querySelectorAll('#' + id_ + ' .column');

  this.handleDragStart = function(e) {
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', 'blah'); // needed for FF.

    // Target element (this) is the source node.
    this.style.opacity = '0.4';
  };

  [].forEach.call(cols_, function (col) {
    // Enable columns to be draggable.
    col.setAttribute('draggable', 'true');
    col.addEventListener('dragstart', this.handleDragStart, false);
  });

})();

// dragEnd
(function() {
  var id_ = 'columns-dragEnd';
  var cols_ = document.querySelectorAll('#' + id_ + ' .column');

  this.handleDragStart = function(e) {
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', this.innerHTML); // needed for FF.

    // Target element (this) is the source node.
    this.style.opacity = '0.4';
  };

  this.handleDragOver = function(e) {
    if (e.preventDefault) {
      e.preventDefault(); // Allows us to drop.
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  };

  this.handleDragEnter = function(e) {
    this.addClassName('over');
  };

  this.handleDragLeave = function(e) {
    // this/e.target is previous target element.
    this.removeClassName('over');
  };

  this.handleDragEnd = function(e) {
    [].forEach.call(cols_, function (col) {
      col.removeClassName('over');
    });

    // target element (this) is the source node.
    this.style.opacity = '1';
  };

  [].forEach.call(cols_, function (col) {
    // Enable columns to be draggable.
    col.setAttribute('draggable', 'true');
    col.addEventListener('dragstart', this.handleDragStart, false);
    col.addEventListener('dragenter', this.handleDragEnter, false);
    col.addEventListener('dragover', this.handleDragOver, false);
    col.addEventListener('dragleave', this.handleDragLeave, false);
    col.addEventListener('dragend', this.handleDragEnd, false);
  });

})();

// dragIcon
(function() {
  var id_ = 'columns-dragIcon';
  var cols_ = document.querySelectorAll('#' + id_ + ' .column');

  this.handleDragStart = function(e) {
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', this.innerHTML);

    var dragIcon = document.createElement('img');
    dragIcon.src = '/static/images/google_logo_small.png';
    e.dataTransfer.setDragImage(dragIcon, -10, -10);

    // Target element (this) is the source node.
    this.style.opacity = '0.4';
  };

  this.handleDragLeave = function(e) {
    // this/e.target is previous target element.

    this.removeClassName('over');
  };

  this.handleDragEnd = function(e) {
    // this/e.target is the source node.

    this.style.opacity = '1';

    [].forEach.call(cols_, function (col) {
      col.removeClassName('over');
    });
  };

  [].forEach.call(cols_, function (col) {
    // Enable columns to be draggable.
    col.setAttribute('draggable', 'true');
    col.addEventListener('dragstart', this.handleDragStart, false);
    col.addEventListener('dragend', this.handleDragEnd, false);
    col.addEventListener('dragleave', this.handleDragLeave, false);
  });

})();

// Almost final example
(function() {
  var id_ = 'columns-almostFinal';
  var cols_ = document.querySelectorAll('#' + id_ + ' .column');
  var dragSrcEl_ = null;

  this.handleDragStart = function(e) {
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', this.innerHTML);

    dragSrcEl_ = this;

    this.style.opacity = '0.4';

    // this/e.target is the source node.
    this.addClassName('moving');
  };

  this.handleDragOver = function(e) {
    if (e.preventDefault) {
      e.preventDefault(); // Allows us to drop.
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  };

  this.handleDragEnter = function(e) {
    this.addClassName('over');
  };

  this.handleDragLeave = function(e) {
    // this/e.target is previous target element.

    this.removeClassName('over');
  };

  this.handleDrop = function(e) {
    // this/e.target is current target element.

    if (e.stopPropagation) {
      e.stopPropagation(); // stops the browser from redirecting.
    }

    // Don't do anything if we're dropping on the same column we're dragging.
    if (dragSrcEl_ != this) {
      dragSrcEl_.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');
    }

    return false;
  };

  this.handleDragEnd = function(e) {
    // this/e.target is the source node.
    this.style.opacity = '1';

    [].forEach.call(cols_, function (col) {
      col.removeClassName('over');
      col.removeClassName('moving');
    });
  };

  [].forEach.call(cols_, function (col) {
    col.setAttribute('draggable', 'true');  // Enable columns to be draggable.
    col.addEventListener('dragstart', this.handleDragStart, false);
    col.addEventListener('dragenter', this.handleDragEnter, false);
    col.addEventListener('dragover', this.handleDragOver, false);
    col.addEventListener('dragleave', this.handleDragLeave, false);
    col.addEventListener('drop', this.handleDrop, false);
    col.addEventListener('dragend', this.handleDragEnd, false);
  });
})();

// Full example
(function() {
  var id_ = 'columns-full';
  var cols_ = document.querySelectorAll('#' + id_ + ' .column');
  var dragSrcEl_ = null;

  this.handleDragStart = function(e) {
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/html', this.innerHTML);

    dragSrcEl_ = this;

    // this/e.target is the source node.
    this.addClassName('moving');
  };

  this.handleDragOver = function(e) {
    if (e.preventDefault) {
      e.preventDefault(); // Allows us to drop.
    }

    e.dataTransfer.dropEffect = 'move';

    return false;
  };

  this.handleDragEnter = function(e) {
    this.addClassName('over');
  };

  this.handleDragLeave = function(e) {
    // this/e.target is previous target element.
    this.removeClassName('over');
  };

  this.handleDrop = function(e) {
    // this/e.target is current target element.

    if (e.stopPropagation) {
      e.stopPropagation(); // stops the browser from redirecting.
    }

    // Don't do anything if we're dropping on the same column we're dragging.
    if (dragSrcEl_ != this) {
      dragSrcEl_.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');

      // Set number of times the column has been moved.
      var count = this.querySelector('.count');
      var newCount = parseInt(count.getAttribute('data-col-moves')) + 1;
      count.setAttribute('data-col-moves', newCount);
      count.textContent = 'moves: ' + newCount;
    }

    return false;
  };

  this.handleDragEnd = function(e) {
    // this/e.target is the source node.
    [].forEach.call(cols_, function (col) {
      col.removeClassName('over');
      col.removeClassName('moving');
    });
  };

  [].forEach.call(cols_, function (col) {
    col.setAttribute('draggable', 'true');  // Enable columns to be draggable.
    col.addEventListener('dragstart', this.handleDragStart, false);
    col.addEventListener('dragenter', this.handleDragEnter, false);
    col.addEventListener('dragover', this.handleDragOver, false);
    col.addEventListener('dragleave', this.handleDragLeave, false);
    col.addEventListener('drop', this.handleDrop, false);
    col.addEventListener('dragend', this.handleDragEnd, false);
  });
})();

0 个答案:

没有答案