以下是 jquery倾斜菜单上拉效果特效代码 的示例演示效果:
部分效果截图:
HTML代码(index.html):
<!DOCTYPE html>
<html>
<head>
<title>jquery倾斜菜单上拉效果</title>
<link rel="stylesheet" type="text/css" href="jstackmenu.css" />
<style>
@font-face {
font-family: 'JournalRegular';
src: url('journal.eot');
src: local('Journal'), local('Journal'), url('journal.woff') format('woff'), url('journal.ttf') format('truetype'), url('journal.svg#Journal') format('svg');
}
body, html {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
#wrapper {
min-height: 500px;
min-width: 900px;
height: 100%;
width: 100%;
position: relative;
}
body {
background-color: #37aac6;
color: #2f370f;
font-family: "JournalRegular";
}
#info {
position: absolute;
top: 80px;
right: 50px;
color: #fff;
}
h1 {
font-weight: normal;
font-size: 120px;
position: relative;
margin: 0;
border-bottom: dashed 6px #3bb5d4;
line-height: 80px;
padding-left: 45px;
}
h1 small {
font-size: 60px;
display: block;
position: absolute;
top: -60px;
left: 0px;
line-height: normal;
}
#info p {
font-size: 30px;
margin: 0;
position: relative;
left: 50px;
}
#info p.download {
font-size: 20px;
margin-top: .5em;
}
#info a {
text-decoration: none;
padding: 0 5px;
color: #fff;
border-bottom: 2px dotted #43cdf0;
line-height: 90%;
display: inline-block;
margin-right: .3em;
}
#info a:hover {
background-color: #3bb3d1;
}
#stack {
width: 68px;
height: 68px;
padding: 0;
position: absolute;
left: 100px;
bottom: 10px;
}
#stack .trigger {
position: absolute;
z-index: 50;
height: 64px;
width: 64px;
padding: 2px;
left: 0;
top: 0;
cursor: pointer;
}
#stack ul {
position: absolute;
top: 0;
left: 18px;
height: 64px;
width: 32px;
bottom: 0;
margin: 0;
padding: 0;
}
#stack ul li {
display: block;
padding: 0px;
width: 32px;
height: 32px;
margin: 6px 0;
}
#stack ul li .title {
display: none;
}
#stack ul li a:hover .title {
display: block;
position: absolute;
right: 38px;
top: 8px;
padding: 1px 8px;
height: 14px;
font-size: 11px;
background-color: #fff;
color: #333;
font-family: Arial, sans-serif;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
border-radius: 8px;
}
#stack ul li img {
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
box-shadow: 0 0 5px #222;
-webkit-box-shadow: 0 0 5px #222;
-moz-box-shadow: 0 0 5px #222;
-o-box-shadow: 0 0 5px #222;
border: none 0;
padding: 2px;
background-color: #fff;
}
#stack ul li a:hover img,
#stack ul li a:hover .title {
box-shadow: 0 0 10px #00a;
-webkit-box-shadow: 0 0 10px #00a;
-moz-box-shadow: 0 0 10px #00a;
-o-box-shadow: 0 0 10px #00a;
}
#stack .trigger {
border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
box-shadow: 0 0 3px #222;
-webkit-box-shadow: 0 0 3px #222;
-moz-box-shadow: 0 0 3px #222;
-o-box-shadow: 0 0 3px #222;
background-color: #7ee6ff;
}
#click-me-message {
position: absolute;
left: 180px;
bottom: 30px;
width: 300px;
padding: 0 0 10px 75px;
font-family: JournalRegular;
font-size: 45px;
background-image: url("arrow.png");
background-position: 0 100%;
background-repeat: no-repeat;
}
#sources {
position: absolute;
bottom: 10px;
right: 50px;
margin: 0;
}
#sources li {
display: block;
list-style-type: none;
}
#sources li:before {
content: "*";
}
#sources a {
text-decoration: none;
color: #6cd5f0;
}
</style>
<script type="text/javascript" src="jquery-ui-1.7.2/development-bundle/jquery-1.3.2.js"></script>
<script type="text/javascript" src="jquery-ui-1.7.2/development-bundle/ui/jquery-ui-1.7.2.custom.js"></script>
<script type="text/javascript" src="jquery-css-transform.js"></script>
<script type="text/javascript" src="jquery-animate-css-rotate-scale.js"></script>
<script type="text/javascript" src="jstackmenu.js"></script>
<script>
jQuery( document ).ready( function( ){
var stack = jQuery( '#stack ul' ).stackmenu( );
jQuery( '#stack .trigger' ).click( function( ){
stack.stackmenu( 'toggle' );
} );
} );
</script>
</head>
<body>
<div id="wrapper">
<table border="0" align="center">
<tr>
<td>
</td>
<td>
</td>
</tr>
</table>
<div id="click-me-message">
点击<img src="icons/heart-32x32.png" title="heart icon" />按钮展开菜单。
</div>
<div id="stack">
<div class="trigger"><img src="icons/heart-64x64.png" alt="share" /></div>
<ul>
<li><a href="http://www.baidu.com/"><span class="title">Twitter</span><img src="icons/twitter-32x32.png" alt="Twitter"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">Digg</span><img src="icons/digg-32x32.png" alt="Digg"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">Facebook</span><img src="icons/facebook-32x32.png" alt="Facebook"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">Reddit</span><img src="icons/reddit-32x32.png" alt="Reddit"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">StumbleUpon</span><img src="icons/stumbleupon-32x32.png" alt="StumbleUpon"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">Delicious</span><img src="icons/delicious-32x32.png" alt="Delicious"></a></li>
<li><a href="http://www.baidu.com/"><span class="title">Flickr</span><img src="icons/flickr-32x32.png" alt="Flickr"></a></li>
</ul>
</div>
</div>
</body>
</html>
JS代码(jstackmenu.js):
/** * Copyright (c) 2010 Seamus P. H. Leahy,http://moronicbajebus.com * * This code is released under the MIT License (which basically means * you are free to use it. * * Permission is hereby granted,free of charge,to any person obtaining * a copy of this software and associated documentation files (the * "Software"),to deal in the Software without restriction,including * without limitation the rights to use,copy,modify,merge,publish,* distribute,sublicense,and/or sell copies of the Software,and to * permit persons to whom the Software is furnished to do so,subject to * the following conditions:* * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS",WITHOUT WARRANTY OF ANY KIND,* EXPRESS OR IMPLIED,INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM,DAMAGES OR OTHER LIABILITY,WHETHER IN AN ACTION * OF CONTRACT,TORT OR OTHERWISE,ARISING FROM,OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * */
/** * * jStackmenu Methods * toggle( [showFlag],[callback] ) * show( [callback] ) * hide( [callback] ) * * jQuery UI inherited methods * enable( ) * disable( ) * destroy( ) * option( ) * * Events * jstackmenushow * jstackmenushowBefore * jstackmenuhide * jstackmenuhideBefore * * Options * show:function( event ){
}
* hide:function( event ){
}
* radius:1000,any positive number * clockwise:true,or false for counter-clockwise * direction:'top',or 'right','bottom','left' * time:500,in milliseconds * */
(function( $ ){
// Some constant valuesvar undefined;
// To make sure undefined is actually undefinedvar radsToDeg = 180 / Math.PI;
var nofn = function(){
}
;
// Some helper functionsvar proxy = function( context,fn ){
return function( ){
if( $.isFunction( fn ) ){
return fn.apply( context,arguments );
}
else{
return context[ fn ].apply( context,arguments );
}
}
;
}
;
var getNow = function( ){
return ( new Date() ).getMilliseconds( );
}
;
var log = function( ){
// do nothing by default// uncomment the next lines for debugging/*debugger;
if( console && console.log ){
console.log.apply( console,arguments );
}
*/
}
;
// Check what CSS features are supported by the browser// Set flags for if transforms and if transition is supported//// The Opera support is commented out because Opera 10.5b does support// transforms,but element.style.OTransform returns an object instead of a// string like the others do. As of writing this,there is not documentation// for how Opera's handling. Perhapes once Opera 10.5 is released,// support can be added.//var docEl = document.documentElement,supportedStyles,supportsTransform = false,supportsTransition = false;
if( docEl && ( supportedStyles = docEl.style ) ){
supportsTransform = !!( typeof supportedStyles.transform == 'string'|| typeof supportedStyles.MozTransform == 'string'|| typeof supportedStyles.WebkitTransform == 'string' );
//||typeof supportedStyles.OTransform == 'string' );
supportsTransition = !!( typeof supportedStyles.transition == 'string'|| typeof supportedStyles.MozTransition == 'string'|| typeof supportedStyles.WebkitTransition == 'string' );
//|| typeof supportedStyles.OTransition == 'string' );
}
//// The functions for basic positioning setup,show and hide with multiple// version depending upon what CSS features are supported//// _________| w/o transform | w/ transform | w/ transition and transform// Position | X | X | -// Show | X | X | X// Hide | X | X | X////// Position Function//// Position without transformvar position_wo_transform = function( ){
// will save the offset in pixels for each elementvar positioning = [];
var is_y = this.options.direction in{
'top':'','bottom':''}
;
this.element.children( ).css({
'left':'','top':'','right':'','bottom':''}
).each( function( ){
var v;
if( is_y ){
v = $( this ).outerHeight( true );
}
else{
v = $( this ).outerWidth( true );
}
v = -1 * v;
positioning.push( v );
}
);
this.positioning = positioning;
}
;
// Position with transformvar position_w_transform = function( ){
// will save for each element an object with three values// 1) t:the value to move the element from the center so that the corners// of the element touch the edge of the circle// 2) a:the angle to rotate the element around the circle// 3) d:the value the element sticks out from the edge of the circlevar positioning = [ ];
// grab all the values that will be used through out making the measurements for each elementvar direction = this.options.direction;
var is_y = this.options.direction in{
'top':'','bottom':''}
;
var is_neg = this.options.direction in{
'left':'','top':''}
;
var neg = is_neg ? -1:1;
var r = parseInt( this.options.radius,10 );
r = r == 0 ? 1:r;
// prevent dividing by zerovar clockwise = this.options.clockwise;
// The common CSS to setup each element// clear out the positioningvar css ={
'top':'','right':'','bottom':'','left':'','transform':'rotate(0deg) translate(0,0)','MozTransform':'rotate(0deg) translate(0,0)','WebkitTransform':'rotate(0deg) translate(0,0)','OTransform':'rotate(0deg) translate(0,0)'}
;
if( direction == 'right' && !clockwise || direction == 'left' && clockwise ){
css.top = 0;
}
if( direction == 'bottom' && !clockwise || direction == 'top' && clockwise ){
css.right = 0;
}
if( direction == 'left' && !clockwise || direction == 'right' && clockwise ){
css.bottom = 0;
}
if( direction == 'top' && !clockwise || direction == 'bottom' && clockwise ){
css.left = 0;
}
this.element.children( ).css( css ).each( function( ){
var $this = $( this );
// t,a and d are the values to be stored for positioning;
dd is the d without marginsvar t,a,d,dd;
if( is_y ){
d = $this.outerHeight( true );
dd = $this.outerWidth( );
}
else{
d = $this.outerWidth( true );
dd = $this.outerHeight( );
}
// set the originvar transRadius = r;
// since the transform-origin of (0,0) is at the top-left corner of the box,// and the radius is from the middle of the edge facing the circle origin,// the width/height needs to be added sometimes to the transform-origin to// make it match up with the circle origin.if( direction in{
'top':'','right':''}
&& clockwise || direction in{
'bottom':'','left':''}
&& !clockwise ){
transRadius += dd;
}
// The transform-origin of the circle for the element// R = transRadius// ___________| top | right | bottom | left// clockwise | +R,50% | 50%,+R | -R,50% | 50%,-R// counter-cw | -R,50% | 50%,-R | +R,50% | 50%,+R//transRadius = direction in{
'top':'','right':''}
? transRadius:-1*transRadius;
transRadius = clockwise ? transRadius:-1 * transRadius;
// now add the 50% to the origin.var origin = is_y ? transRadius+'px 50%':'50% '+transRadius+'px';
$this.css({
'transformOrigin':origin,'MozTransformOrigin':origin,'OTransformOrigin':origin,'WebkitTransformOrigin':origin}
);
// A line from the center of the circle intersects at a right angle// with the middle of the element's edge facing. Because of this,// we need to align all the elements to their facing edge middle// so that their transform-radius are all at the same X,Y with respect// to the document overall.$this.css( direction,(d*-0.5)+'px' );
// Calculate the angle// Note:The corners of the element's facine edge are on the edge of the// circle which means the rest of the edge is inside of the circle.//// opposite = half of the length of the element's facing edge// hypotenuse = the radius of the circle// adjacent = will be t,r-t is the amount to adjust it latera = Math.asin( d/r*0.5 );
// a = arcsine( opposite/hypotenuse )t = Math.cos( a ) * r;
// cosine( a ) = adjacent / hypotenuse;
adjacent = ( adjacent / hypotenuse ) * hypotenusea = a*radsToDeg;
// convert a to degrees from radians// Get the amount to move the element towards the circle center so// that the corners of the element are one the circle's edge.// ___________| top | right | bottom | left// clockwise | +t | -t | -t | +t// counter-cw | -t | +t | +t | -tt = r-t;
t = clockwise? t:-1*t;
if( is_y ){
t = -1*t*neg;
t = t+'px,0';
}
else{
t = t*neg;
t = '0,'+t+'px';
}
positioning.push({
t:t,a:a,d:d}
);
}
);
this.positioning = positioning;
}
;
var position_function = supportsTransform ? position_w_transform:position_wo_transform;
// Show Function// Show without transformvar show_wo_transform = function( ){
// data needed to setup the animationvar time = this.animationDuration;
var dir = this.options.direction;
var positioning = this.positioning;
var offset = 0;
// a running value for the offsetvar callback = proxy( this,'_show' );
var i = this.element.children( ).size( );
this.element.children( ).stop( ).css( 'display','' ).each( function( i ){
offset += positioning[ i ];
var ani ={
'opacity':1}
;
ani[ dir ] = offset;
$( this ).animate( ani,time,'swing',function( ){
// run callback after the last item finishes animatesif( --i == 0 ){
callback( );
}
}
);
}
);
}
;
// Show with transform without transitionvar show_w_transform = function( ){
// kill the currently running animationthis.element.children( ).stop( );
var positioning = this.positioning;
var angle = 0;
// running value for the anglevar c = this.options.clockwise ? 1:-1;
var time = this.animationDuration;
var callback = proxy( this,'_show' );
this.element.children( ).css( 'display','' ).each( function( i ){
var p = positioning[ i ];
angle += c*p.a;
$( this ).css( 'transform','translate('+(p.t)+')' ).animate({
'rotate':angle,'opacity':1}
,time,callback);
callback = nofn;
// only need to run the callback onceangle += c*p.a;
// add the angle again since the angle is for the middle of the element}
);
}
;
// Show with transform and transitionvar show_w_transition = function( ){
clearTimeout( this._timeout );
var positioning = this.positioning;
var angle = 0;
// running value for the anglevar c = this.options.clockwise ? 1:-1;
var time = this.animationDuration;
var timeSec = (time/1000)+'s';
var aniBase ={
'display':'','transition':'transform '+timeSec+',opacity '+timeSec,'MozTransition':'-moz-transform '+timeSec+',opacity '+timeSec,'WebkitTransition':'-webkit-transform '+timeSec+',opacity '+timeSec,'OTransition':'-o-transform '+timeSec+',opacity '+timeSec}
;
this.element.children( ).each( function( i ){
var p = positioning[ i ];
angle += c*p.a;
var trans = 'rotate('+angle+'deg) translate('+p.t+')';
var ani ={
'opacity':1,'transform':trans,'MozTransform':trans,'WebkitTransform':trans,'OTransform':trans}
;
$( this ).css( aniBase );
// Cannot apply transition and transform at the same time.// If you do,then the transition are ignored. Wait a really// short moment after transition,then apply the transform.setTimeout( proxy( this,function( ){
$( this ).css( ani );
}
),10 );
angle += c*p.a;
}
);
// Set a timer to finish up the animation and run the callbacksthis._timeout = setTimeout( proxy( this,'_show' ),time );
}
;
var show_function = supportsTransform ? ( supportsTransition ? show_w_transition:show_w_transform ):show_wo_transform;
// Hide Functions// Hide without transformvar hide_wo_transform = function( ){
var ani ={
'opacity':0}
;
ani[ this.options.direction ] = 0;
var callback = proxy( this,'_hide' );
var i = this.element.children( ).size( );
this.element.children( ).stop( ).animate(ani,this.animationDuration,function( ){
$(this).css( 'display','none' );
if( --i == 0 ){
callback( );
}
}
);
}
;
// Hide with transformvar hide_w_transform = function( ){
// kill the currently running animationthis.element.children( ).stop( );
var callback = proxy( this,'_hide' );
var i = this.element.children( ).size( );
this.element.children( ).animate({
'rotate':0,'opacity':0}
,this.animationDuration,function( ){
$( this ).css( 'display','none' );
if( --i == 0 ){
callback( );
}
}
);
}
;
// Hide with transition and transformvar hide_w_transition = function( ){
clearTimeout( this._timeout );
var timeSec = (this.animationDuration/1000)+'s';
// convert from milliseconds to secondsvar aniBase ={
'transition':'transform '+timeSec+',opacity '+timeSec,'MozTransition':'-moz-transform '+timeSec+',opacity '+timeSec,'WebkitTransition':'-webkit-transform '+timeSec+',opacity '+timeSec,'OTransition':'-o-transform '+timeSec+',opacity '+timeSec}
;
this.element.children( ).css( aniBase );
setTimeout( proxy( this,function( ){
this.element.children( ).css({
'opacity':0,'transform':'rotate(0deg) translate(0,0)','MozTransform':'rotate(0deg) translate(0,0)','WebkitTransform':'rotate(0deg) translate(0,0)','OTransform':'rotate(0deg) translate(0,0)'}
);
}
),10 );
this._timeout = setTimeout( proxy( this,function( ){
this.element.children( ).css( 'display','none' );
this._hide( );
}
),this.animationDuration );
}
;
var hide_function = supportsTransform ? ( supportsTransition ? hide_w_transition:hide_w_transform ):hide_wo_transform;
$.widget( 'ui.stackmenu',{
_init:function( ){
// basic CSS setupthis.element.addClass( 'ui-stackmenu' );
this.element.children( ).addClass( 'ui-stackmenu-item' ).css({
'opacity':0,'display':'none'}
).css( this.options.direction,0 );
this.isShowing = false;
// The time the animation started and// the amount of the time the animation was to run.this.animationStartTime = false;
this.animationDuration = this.options.time;
// for saving the callbacks passed into the methodthis._hideCallbackFn = nofn;
this._showCallbackFn = nofn;
// Calculate positionposition_function.call( this );
}
,/** * Shows the stack menu. */
show:function( callback ){
this.toggle( true,callback );
}
,/** * Hides the stack menu. */
hide:function( callback ){
this.toggle( false,callback );
}
,/** * Toggle the display of the stack menu. * * $( el ).stackmenu( 'toggle' [,show] [,callback] );
* * @param show (optional) - a boolean flag if true,then it shows;
if false,* then it hides;
if undefined (not passed),then it will toggle * @param callback (optional) - a function for the callback when finished running */
toggle:function( show,callback ){
if( this.options.disabled == true ){
return;
}
// sort out the parametersif( $.isFunction( show ) ){
callback = show;
show = undefined;
}
var nextState = show === undefined? !this.isShowing:show;
callback = $.isFunction( callback )? callback:nofn;
// quit early if we are already in the stateif( nextState == this.isShowing ){
return;
}
// save the method callback functionsthis._hideCallbackFn = nofn;
this._showCallbackFn = nofn;
// calculate the animation timeif( this.animationStartTime === false ){
// There is no animation running,so use the whole timethis.animationDuration = this.options.time;
}
else{
// Since there is time on the clock,then we are in mid-animation.// The time to animate back is the same as it took to animate// to the current position.var now = getNow( );
var timeSince = now - this.animationStartTime;
timeSince = timeSince > this.options.time? this.options.time:timeSince;
this.animationDuration = timeSince;
}
try{
// now either show or hideif( nextState ){
// Showthis._trigger( 'showBefore',{
}
,[this.element.get( 0 )] );
this._showCallbackFn = callback;
show_function.call( this );
}
else{
// Hidethis._trigger( 'hideBefore',{
}
,[this.element.get( 0 )] );
this._hideCallbackFn = callback;
hide_function.call( this );
}
}
catch( err ){
log( err );
}
this.isShowing = nextState;
}
,/** * Removes the instance from the encapsulated DOM element,* which was stored on instance creation. */
destroy:function( ){
this.element.removeClass( 'ui-stackmenu' );
this.element.children( ).removeClass( 'ui-stackmenu-item' ).css({
'opacity':'','display':'','top':'','right':'','bottom':'','left':'','transformOrigin':'','MozTransformOrigin':'','OTransformOrigin':'','WebkitTransformOrigin':'','transform':'','MozTransform':'','OTransform':'','WebkitTransform':'','transition':'','MozTransition':'','WebkitTransition':'','OTransition':''}
);
// Final clean up$.widget.prototype.destroy.apply( this,arguments );
}
,/** * Gets or sets an option for this instance */
option:function( key,value ){
// If no value is given,then have the default action happenif( value === undefined ){
return $.widget.prototype.option.apply( this,arguments );
}
// Short circut if nothing is changingif( this.options[ key ] === value ){
return;
}
if( key in{
'direction':'','clockwise':'','radius':''}
){
if( key == 'direction' && !( value in{
'top':'','right':'','bottom':'','left':''}
) ){
return;
// invalid direction}
var reposition = proxy( this,position_function );
if( this.isShowing ){
var reshow = proxy( this,'show' );
var changeOption = proxy( this,function( ){
this.options[ key ] = value;
}
);
this.hide( function( ){
changeOption( );
reposition( );
setTimeout( reshow,10 );
// Place in a setTimeout to resolve sync error// that causes the first element's opacity not to animate.}
);
}
else{
this.options[ key ] = value;
reposition( );
}
return value;
}
if( key == 'time' ){
this.options.time = value;
return;
}
return $.widget.prototype.option.apply( this,arguments );
}
,/** * Handles the cleanup for "show" */
_show:function( ){
this.animationStartTime = false;
this._call_callbacks( 'show' );
}
,/** * Handles the cleanup for "hide" */
_hide:function( ){
this.animationStartTime = false;
this._call_callbacks( 'hide' );
}
,/** * Handles all the event callbacks for an event type */
_call_callbacks:function( type ){
// avoid bad code in the callbacks from taking down the widgettry{
if( this['_'+type+'CallbackFn'] ){
var event = $.Event({
}
);
event.type = this.widgetEventPrefix + type;
this['_'+type+'CallbackFn'].call( this.element.get( 0 ),event,this.element.get( 0 ) );
}
}
catch( err ){
}
this._trigger( type,{
}
,this.element.get( 0 ) );
}
}
);
$.extend( $.ui.stackmenu,{
defaults:{
'direction':'top','clockwise':true,'radius':'1000px','time':500}
}
);
}
)( jQuery );
CSS代码(jstackmenu.css):
.ui-stackmenu{position:relative;}
.ui-stackmenu-item{position:absolute;}