为 android, ipad, surface 写的swipe旋转菜单效果, 代码很乱, 没有整理, 将就着看.
复制成html直接运行就可以.
<!DOCTYPE HTML>
<html>
<head>
<title> New Document </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script>
jQuery(function(){
"use strict";
var c = {
stylePosition: $("#navSliderBlockStyle"),
radius: window.screen.width / 10,
pathCount: 20, // 动画路径数量
pathArray: null, // 路径座标
items: $("#navSliderBlock > div"),
itemCount: 0}; // 图标链接数量
c.itemCount = c.items.length;
var CP="-webkit-", // css transform name
screen = [window.screen.width, window.screen.height], // 屏幕大小
superSwipe = window.screen.width*0.6, // 长距离判断
duration = 350;
if (window.MSCSSMatrix != null){CP="-ms-";}
c.transform = function(x,y,lite){
if(lite===true){
return "translate("+x+"px,"+y+"px)";
}else{
return CP+"transform:translate("+x+"px,"+y+"px)";
}
};
/**
* 按点阵索引获取需要的百分比, 索引圆从下方,逆时针计算
* hor 没有设置的时候, 按 0 <- 1/2 -> 1,一半的时候是最低地, 索引 0 位置的达到100, 即圆的下方
* hor == true 设置为真的时候,按 0 -> 1/4 <- 1/2 -> 3/4 <- 1 排列, 索引 1/4, 3/4 为最高100, 即圆的左右两边最高
*/
c.getLevel = function(pointIndex, hor){
var oriIndex = pointIndex;
var half = c.pathCount / 2;
if (hor == undefined){
pointIndex -= half;
pointIndex = Math.abs(pointIndex);
return Math.ceil(pointIndex / half * 100);
}else{
var halfAgain = half / 2;
pointIndex -= half;
pointIndex = Math.abs(pointIndex);
pointIndex -= halfAgain;
pointIndex = Math.abs(pointIndex);
pointIndex = halfAgain - pointIndex;
pointIndex *= half < oriIndex ? -1 : 1;
return pointIndex / halfAgain * 100;
}
};
c.getIndex = function(pointIndex){
return c.getLevel(pointIndex);
};
c.getAlpha = function(pointIndex){
return c.getLevel(pointIndex) / 100 * 0.5 + 0.5;
};
c.getSkewX = function(pointIndex){
return Math.ceil(c.getLevel(pointIndex, true) / -10);
};
// 查找当前元素translate偏移座标最近的关键帧
c.currentElePoint = function(element){
var curTransform = null;
if (window.MSCSSMatrix){
curTransform = new MSCSSMatrix(window.getComputedStyle(element).msTransform);
}else if(window.WebKitCSSMatrix){
curTransform = new WebKitCSSMatrix(window.getComputedStyle(element).webkitTransform);
}
var xy = [curTransform.m41,curTransform.m42];
var pathPoint = 0, last = Math.abs(c.pathArray[0][0] - xy[0]) + Math.abs(c.pathArray[0][1] - xy[1]), temp;
for(var i = 1 ;i < c.pathArray.length; i++){
temp = Math.abs(c.pathArray[i][0] - xy[0]) + Math.abs(c.pathArray[i][1] - xy[1]);
if (last > temp){
last = temp;
pathPoint = i;
}
}
return pathPoint;
};
// 查找最近的链接点
c.keyPath = function(pathPoint){
pathPoint = pathPoint % c.pathCount;
pathPoint = Math.round(pathPoint / (c.pathCount / c.itemCount));
if (pathPoint == c.itemCount){pathPoint = 0;}
pathPoint *= (c.pathCount / c.itemCount);
if (pathPoint == c.pathCount){pathPoint = 0;}
return pathPoint;
};
c.keyPathDistance = function(start, away){
var space = Math.ceil(c.pathCount / c.itemCount);
var offset = ( start + Math.abs(away) ) % space;
if (away < 0){
away -= offset;
}else{
away += offset;
}
/*
var offset = ( start + away ) % space;
if (offset > space / 2){
offset = space - offset;
}
away += offset;
*/
return away;
};
c.addStyle = function(style){
c.stylePosition.append(style);
};
c.clearStyle = function(){
c.stylePosition.empty();
}
// strength 单位是 数组pathArray长度之一
c.animateSet = function(jq, duration, strength, release){
var name = "animate_"+jq.index()+"_"+ +new Date() // keyframe 名
, code = "@"+CP+"keyframes " + name + "{" // keyframe style
, currentPoint = c.currentElePoint(jq[0]) // 当前对象所在位置
, direction = strength > 0 ? 1 : -1;
strength = Math.abs(strength);
// t_b 开始值,t_c 结束值, t_d 目标时间, t_t 不断递增的当前时间
var t_b=0,t_c=100,t_d=strength,t_t=0, nextPoint = currentPoint, lastPoint = 0, percent = null;
//debug("<br />"+jq.index()+"开始:", false);
while(t_t <= t_d){
//debug(nextPoint, false);
percent = ease(t_t,t_b,t_c,t_d);
percent = Math.ceil(percent * 100);
percent = percent / 100;
lastPoint = nextPoint;
code += percent + "%{"+CP+"transform:"+
c.transform(c.pathArray[nextPoint][0], c.pathArray[nextPoint][1], true)
+" skew("+c.getSkewX(nextPoint)+"deg)"+
"; z-index:"+c.getIndex(nextPoint)+
"; skewX:("+c.getSkewX(nextPoint)+"deg)"+
"; opacity:"+c.getAlpha(nextPoint)+";}\n";
t_t++;
nextPoint += direction;
if (nextPoint < 0){
nextPoint = c.pathArray.length - 1;
}else if(nextPoint >= c.pathArray.length){
nextPoint = 0;
}
}
function ease(t,b,c,d){
//return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
return c*(t/=d)*t*t + b;
//return c*((t=t/d-1)*t*t + 1) + b;
}
c.addStyle(code + "}");
jq.css(CP+"animation", name + " " + duration +"ms linear")
.css(CP+"transform", c.transform(c.pathArray[lastPoint][0], c.pathArray[lastPoint][1], true)
+" skew("+c.getSkewX(lastPoint)+"deg)")
.css("z-index",c.getIndex(lastPoint))
.css("opacity",c.getAlpha(lastPoint));
};
c.resetAll = function(){
if (c.itemCount % c.pathCount != 0){
var d = Math.floor(c.pathCount / c.itemCount);
c.pathCount = c.itemCount * (d+1);
}
c.pathArray = c.circlePath(c.pathCount, c.radius);
var per = c.pathCount / c.itemCount;
$.each(c.items, function(i,v){
var point = per*i;
$(v)
.css( CP+"transform", c.transform( c.pathArray[point][0], c.pathArray[point][1], true )
+" skew("+c.getSkewX(point)+"deg)" )
.css("z-index",c.getIndex(point))
.css("opacity",c.getAlpha(point));
});
};
c.animateAllByLevel = function(x){
c.clearStyle();
var addPoint = c.pathCount / c.itemCount;
var direction = x > 0 ? 1 : -1;
var firstPoint = c.currentElePoint(c.items[0]);
var walkPoint = firstPoint;
walkPoint+=direction;
while(walkPoint % addPoint != 0){
walkPoint+=direction;
}
if (Math.abs(x) >= superSwipe){
walkPoint+=direction;
while(walkPoint % addPoint != 0){
walkPoint+=direction;
}
}
walkPoint = walkPoint - firstPoint
$.each(c.items,function(i,v){
//var currentPoint = c.currentElePoint(v);
//addPoint = c.keyPathDistance(currentPoint, addPoint);
//debug(currentPoint + "; " + addPoint);
c.animateSet($(v), duration, walkPoint);
});
};
c.animateAll = function(strength){
$.each(c.items,function(i,v){
c.animateSet($(v), 500, strength);
});
};
// 从180度开始,逆时针
c.circlePath = function(points, radius){
var xys = [], r = 1, numDeg = 0, style = '';
var perDeg = 360 / points;
for(var i = 0; i < points; i++){
numDeg = perDeg*i*Math.PI/180;
xys[i] = [Math.round(Math.sin(numDeg) * radius), Math.round(Math.cos(numDeg) * radius * 0.2)];
}
return xys;
};
//------------------------------------------
// 事件
//------------------------------------------
var eventTime = {
lock: 0, // 0 无, 1 按下或移动, 2 动画
startTime: null,
lastTime: null, // 最近记录的时间
startX:null, // 开始的位置
currentX:0, // 最近记录的鼠标位置
px:30, // 确认滚动需要的距离
timeCheck: 200, // 最小侦察移动事件时间段
clear: function(){
eventTime.currentX = null;
eventTime.startX = null;
eventTime.startTime = null;
eventTime.lastTime = null;
},
touch: function(x){
//if (eventTime.lock == 2){return;}
if (eventTime.startTime == null){
eventTime.startTime = +new Date();
eventTime.startX = x;
}
eventTime.currentX = x;
eventTime.lastTime = +new Date();
},
move: function(x){ // 判断是不是可以发生移动,返回强度(单位:1/c.pathCount)
if (eventTime.isMoving() == false){ return; }
var currentTime = +new Date();
var deltaX = x - eventTime.currentX;
var deltaTime = currentTime - eventTime.lastTime
//if (eventTime.timeCheck < deltaTime || Math.abs(deltaX) > eventTime.px){
if (Math.abs(deltaX) > eventTime.px){
var strength = 0;
strength = ( (deltaX / screen[0]) * c.itemCount ) * c.pathCount;
eventTime.touch(x);
c.animateAll(strength);
}
return 0;
},
moveDirect: function(x){
if ( eventTime.lock == 2 || eventTime.lock == 0 ){ return; }
//if ( eventTime.startX == null ){ eventTime.startX = x; }
var deltaX = x - eventTime.startX;
//debug("startX:" + eventTime.startX + "; x = " + x);
if (Math.abs(deltaX) >= eventTime.px){
eventTime.lock = 2;
c.animateAllByLevel(deltaX);
eventTime.clear();
window.setTimeout( function(){
if (eventTime.lock == 2){
eventTime.lock = 1;
eventTime.startX = x;
}
}, duration );
}
}
};
function touchstart(e){
if ( e.touches && e.touches.length > 1 || e.scale && e.scale !== 1) return;
e = eventReset(e);
eventTime.lock = 1;
eventTime.touch( e.reset.pageX );
}
function touchend(e){
eventTime.moveDirect(eventTime.currentX);
eventTime.clear();
eventTime.lock = 0;
}
function touchmove(e){
e.preventDefault();
if ( e.touches && e.touches.length > 1 || e.scale && e.scale !== 1) return;
e = eventReset(e);
eventTime.currentX = e.reset.pageX;
eventTime.moveDirect(eventTime.currentX);
//c.currentTouchX = e.pageX;
}
function eventReset(event){
event.reset = {};
if (event.touches && event.touches.length > 0){
event.reset.pageX = event.touches[0].pageX;
event.reset.pageY = event.touches[0].pageY;
}else{
event.reset.pageX = event.pageX;
event.reset.pageY = event.pageY;
}
return event;
}
//window.setInterval(function(){eventTime.move(c.currentTouchX);}, 50);
c.initEvent = function(){
$.each(["touchstart","MSPointerDown"],function(i,v){
document.addEventListener(v, touchstart, false);
});
$.each(["touchend","MSPointerUp"],function(i,v){
document.addEventListener(v, touchend, false);
});
$.each(["touchmove","MSPointerMove"],function(i,v){
document.addEventListener(v, touchmove, false);
});
$.each(["selectstart", "dragstart"], function(i,v){
document.body.addEventListener(v, function(e) { e.preventDefault(); }, false);
}
);
/* debug */
if (navigator.appVersion.indexOf("Windows NT")> -1){
document.addEventListener("mousedown", touchstart, false);
document.addEventListener("mouseup", touchend, false);
document.addEventListener("mousemove", touchmove, false);
}
/**/
};
window.controller = c;
c.resetAll();
c.initEvent();
});
function debug(c, falseLine){
if (falseLine == false)
{
$("#debugContainer").prepend(c + ";");
}else{
$("#debugContainer").prepend(c +"<br />");
}
}
</script>
<style>
html body{
overflow: hidden; /* 禁用平移 */
-ms-content-zooming: none; /* 禁用缩放 */
-ms-touch-action: none; /* 禁用系统touch事件 */
}
#navSliderBlock{position:absolute;left:300px;top:160px;}
#navSliderBlock > div{position:absolute;}
</style>
<style id="navSliderBlockStyle">
</style>
</head>
<body style="background:#333;">
<div id="navSliderBlock">
<div><img src="http://2c.zol-img.com.cn/product/110_800x600/714/ce36KAuUVsJdY.jpg" width="200" /></div>
<div><img src="http://2c.zol-img.com.cn/product/110_800x600/714/ce36KAuUVsJdY.jpg" width="200" /></div>
<div><img src="http://2c.zol-img.com.cn/product/110_800x600/714/ce36KAuUVsJdY.jpg" width="200" /></div>
<div><img src="http://2c.zol-img.com.cn/product/110_800x600/714/ce36KAuUVsJdY.jpg" width="200" /></div>
</div>
<div id="debugContainer" style="width:500px;height:300px;overflow:hidden;">
</div>
</body>
</html>