primers
I have been working on a demo of a sliding wheel these days, and there are similar examples on the Internet, but it is better to rewrite their code according to the needs of the boss than to completely meet the needs of the plug-in. Nice idea, but beginner on the road…
Effect link at the end of the text
demand
The demo is very simple. There are three rotating places, the inner disk, the outer disk and the pointer. The intersection of the sets on these three sets produces a link, which is jumped through the middle button.
This demand at first glance looks old simple old simple, but as a vegetable chicken on the road for the first time, comparable to driving bumper cars, broken head and blood.
Analysis of the
Before doing, but also according to their own understanding to write rotation Angle problem:
- The way to rotate the turntable is to set the center of the circle as the origin of rotation, and dynamically modify the rotation Angle;
- Calculate the Angle between the two points and the center point in TouchMove.
Those are the two things you need to know about rotations in general, but there are a lot of problems with actually calculating angles.
Calculation Angle of curve 1
The first mathematical method used to calculate the Angle is the inverse function. There are two ways to express the inverse function in JS:
- Math.atan
- Math.atan2
To tell the truth, there is no difference between them in this demo, but compared with atAN, when y is very large, there will be errors, so I resolutely choose ATan2
(function($){
$.fn.CompassRotate=function(options){
var defaults={
trigger:document,
centerX:0,
centerY:0,
debug:false
},_this=this;
var ops=$.extend(defaults,options);
function Init(){// Initialize the center pointif(ops.centerX==0 && ops.centerY==0){
ops.centerX=_this.offset().left+_this.width()/2;
ops.centerY=_this.offset().top+_this.height()/2
}
$(ops.trigger).on("touchstart".function(event){
$(document).on("touchmove",movehandle);
});
$(ops.trigger).on("touchend".function(event) {
$(document).unbind("touchmove"); }); } // Handle events when the mouse movesfunction movehandle(event){
var touch = event.originalEvent.targetTouches[0];
var dis = angle(ops.centerX,ops.centerY,touch.pageX, touch.pageY);
if(ops.debug) console.log(ops.centerX+"-"+ops.centerY+"|"+touch.pageX+"-"+touch.pageY+""+dis); rotate(dis); } // Calculate the Angle of the two points on the pagefunctionangle(centerx, centery, endx, endy) { var diff_x = endx - centerx, diff_y = endy - centery; var c=360 * Math.atan2(diff_y , diff_x) / (2 * Math.PI); c=c<=0? (360+c):c;returnc; } // Set the Anglefunction rotate(angle,step){
$(_this).css("transform"."translate3d(-50%,-50%,0) rotateZ(" + angle + "deg)"); } // Pointers point to Angle changes and generate urlsfunction angleOrLink(angle) {
Angle = angle;
}
Init();
};
})(jQuery);
$(".box").CompassRotate({trigger:$(".box"),debug:true});Copy the code
It is better to paste the code to make it easier to read.
Change of area set in turn 2
As anyone who has ever done a turntable lottery knows, each prize corresponds to a set of angles, the Angle the pointer turns [0,360] to see which set it falls on, and this turntable is the same, but the only difference is that the set of inner and outer disks is changeable, not fixed.
var insideCollection = [
{
/* GC+S1 */
min: 270,
max: 360,
reverse: false,
mark: 's1gc'
},
{
/* BC+AT */
min: 0,
max: 45,
reverse: false,
mark: 'bcat'
},
{
/* BC+GT */
min: 45,
max: 90,
reverse: false,
index: 'bcgt'
},
{
/* mCRC+FOLFOX */
min: 90,
max: 180,
reverse: false,
mark: 'mCRC'}, {/* eCRC+ chemotherapy */ min: 225, Max: 270, reverse:false,
mark: 'eCRC1'
},
{
/* eCRC+FOLFOX */
min: 180,
max: 225,
reverse: false,
mark: 'eCRC2'}]; Var outsideCollection = [{/* research */ min: 270, Max: 342, reverse:false,
mark: 'study'}, {/* guide */ min: 342, Max: 54, reverse:true,
mark: 'guide'}, {/* reverse */ min: 54, Max: 126, reverse:false,
mark: 'competing goods'}, {/* data */ min: 126, Max: 198, reverse:false,
mark: 'information'}, {/* mechanism */ min: 198, Max: 270, reverse:false,
mark: 'mechanisms'}];Copy the code
Min, Max, of course, is a set, but what does this reverse property represent? When dividing the interval, the Angle change is always 0-360°, “0==360”. So, what can be expressed when the interval of a set is [340,25]? And, of course, there’s only one set of each rotation that faces this, so I’m going to use a property to represent this interval across angles.
// Rotary table interval distribution changesfunction collectionChange(angle,array) {
array.forEach(function (ele,index) {
ele.reverse = false;
});
array.forEach(function (ele,index) {
ele.min = (Number(angle)+Number(ele.min))%360;
ele.max = (Number(angle)+Number(ele.max))%360;
if(ele.min > ele.max){
ele.reverse = true; }}); console.log(array) }Copy the code
Mark: I don’t need to talk about it.
The code posted here has basically completed the general function. Finally, the link is matched according to the mark of the inner and outer disk when clicking on the link:
$('#compass_5').on('click'.function(){ var angle = Angle; Var link = contrast(insideCollection) + contrast(outsideCollection); console.log(link);function contrast(array){
var link ;
array.forEach(function (ele,index) {
if(angle >= ele.min%360 && angle <= (ele.max%360 ==0? 360:ele.max%360)){ link = ele.mark; }else if(ele.reverse){
if(angle<=360 && angle >=270){
if(angle >= ele.min%360 && angle <= (ele.max%360 ==0?360:ele.max%360+360)){
link = ele.mark;
}
}
else if(angle>=0&&angle<=90){
if(angle+360 >= ele.min%360 && angle+360 <= (ele.max%360 ==0? 360:ele.max%360+360)){ link = ele.mark; }}}});returnlink; }})Copy the code
Bend 3 the King of the Pit Kings
It says that the function is basically completed, it is just to choose one point on the roulette wheel step by step to rotate. If you rotate it in different positions for many times, you find that the whole roulette wheel is paralyzed — Mark cannot match.
To do the first step in this demo, I started with a simple pointer dial hands, the basic operation of rotation of the complete a pointer, so the whole process is feasible, because the pointer fixed the rotation center of the circle, it’s optional area just hot piece, so no more for buried behind the hole.
Inverse function calculation Angle problem
var c=360 * Math.atan2(diff_y , diff_x) / (2 * Math.PI);
// c [-180,180];
c=c<=0?(360+c):c;
// c [0,360];Copy the code
Calculating angles this way is fine for a pointer, but can be a nightmare on a turntable.
Because it’s not sure where it’s going to land.
As a result, when you point to different areas, it will give you a value for the Angle of rotation directly, so it will always be the middle line that follows your finger.
Pit King link (view it in Chrome debugger)
The pit encountered by such operation is that the starting position will change accordingly with the change of finger, so the interval of each region should also change accordingly. Therefore, a step operation should be carried out in touchStart to calculate the Angle between the last end position and the current position, and then change the interval change again.
$(ops.trigger).on("touchstart".function(event){ var touch = event.originalEvent.changedTouches[0]; var dis = angle(ops.centerX,ops.centerY,touch.pageX, touch.pageY); startAngle = dis; // The Angle after sliding the turntable again is not consistent with the last end Angle (inner disk)if(startAngle ! = ops.initAngle_in && ops.initAngle_in ! = 0) {if(ops.initAngle_in>startAngle){
insideDishAngleChangeSecondary((Number(startAngle+360)-ops.initAngle_in));
}
else if(ops.initAngle_in< startAngle){
insideDishAngleChangeSecondary((startAngle-ops.initAngle_in));
}
}
$(document).on("touchmove",movehandle);
});Copy the code
Modified compass
The last version of the compass basic operation is wrong, produced a series of bugs, although have overcome a series of bugs, but still are in the pit, but the pit is parallel to move, the pit dug in a direction to continue to dig it.
Forks in the road
When I re-examine my thinking, I realize how stupid I have been.
In the previous algorithm, the start point is 0 and the end point is pointed to. When touchMove assigns the Angle value to the compass, the Angle formed by the two points is directly assigned to the compass rotate(Angle). The next set of actions will pay for this place, whether it’s writing a new function to record the Angle change before the Touchmove starts, or assigning the compass distribution interval to the center point as the finger rotates.
Rethinking the compass turn, with the previous foreshadowing, so the train of thought has become particularly clear. To implement this requirement, there are three recorded data:
- actual_angle: The Angle between the starting and ending points and the center point, which is the degree of each rotation of the compass, and this value should be accumulated;
- addAngle: The value that needs to be added to the compass distribution interval at the end of each rotationactual_angle;
- startAngle :touchstartWhen the finger points to the landing point, namely the starting point.
$.fn.RotateH=function(options){
var defaults={
trigger:document,
centerX:0,
centerY:0,
debug:false},_this=this; var ops=$.extend(defaults,options); var startAngle,addAngle, actual_angle = 0; / / initializationfunction Init(){// Initialize the center pointif(ops.centerX==0 && ops.centerY==0){
ops.centerX=_this.offset().left+_this.width()/2;
ops.centerY=_this.offset().top+_this.height()/2
}
$(ops.trigger).on("touchstart".function(event){
var touch = event.originalEvent.changedTouches[0];
var dis = angle(ops.centerX,ops.centerY,touch.pageX, touch.pageY);
startAngle = dis;
$(document).on("touchmove",movehandle);
});
$(ops.trigger).on("touchend".function(event) { var touch = event.originalEvent.changedTouches[0]; var dis = angle(ops.centerX,ops.centerY,touch.pageX, touch.pageY); // The Angle of each rotationif(dis >=startAngle){// actual_angle += (dis-startAngle); AddAngle = (dis-startangle); }else if(dis <startAngle){
actual_angle += (dis+360-startAngle);
addAngle = (dis+360-startAngle)
}
if(ops.collection) collectionChange(addAngle,ops.collection);
else angleOrLink(dis);
$(document).unbind("touchmove"); }); } // Handle events when the mouse movesfunctionMovehandle (event) {/ / Angle between two points for var touch = event. The originalEvent. TargetTouches [0]. var dis = angle(ops.centerX,ops.centerY,touch.pageX, touch.pageY); var Angle = 0;if(ops.debug) console.log(ops.centerX+"-"+ops.centerY+"|"+touch.pageX+"-"+touch.pageY+""+dis);
if(ops.pointer){
rotate(dis);
}
else{// The Angle of each rotationif(ops.debug) {
console.log("-- -- -- -- -- -- -- -- -- -- --");
console.log('Last rotation Angle:'+actual_angle);
}
if(dis >=startAngle){
Angle = dis-startAngle;
if(ops.debug) {
console.log("Rotation Angle:"+Angle);
console.log("Actual rotation Angle:"+(Angle+actual_angle));
}
rotate((Angle+actual_angle));
}
else if(dis <startAngle){
Angle = dis-startAngle+360;
if(ops.debug){
console.log("Rotation Angle:" + Angle);
console.log("Actual rotation Angle:"+(Angle+actual_angle)); } rotate((Angle+actual_angle)); }}} // Calculate the Angle of the two points on the pagefunctionangle(centerx, centery, endx, endy) { var diff_x = endx - centerx, diff_y = endy - centery; var c=360 * Math.atan2(diff_y , diff_x) / (2 * Math.PI); c=c<=0? (360+c):c;returnc; } // Set the Anglefunction rotate(angle,step){
$(_this).css("transform"."translate3d(-50%,-50%,0) rotateZ(" + angle + "deg)"); } // Dial interval distribution changesfunction collectionChange(angle,array) {
array.forEach(function (ele,index) {
ele.reverse = false;
});
array.forEach(function (ele,index) {
ele.min = (Number(angle)+Number(ele.min))%360;
ele.max = (Number(angle)+Number(ele.max))%360;
if(ele.min > ele.max){
ele.reverse = true; }});if(ops.debug) console.log(array); } // The Angle of the pointerfunction angleOrLink(angle) {
Angle = angle;
}
Init();
};Copy the code
Perfectcompass.github. IO (this is an iPad demo, check it out in Chrome debugger)
Github address: github.com/suiyang1714…
conclusion
This demo was finally made by myself with time, without particularly high technical content, mainly thinking in this process. If I had figured out what to do at first, I wouldn’t have taken so many turns. My initial idea was that the compass could rotate first, then consider the interval variation, and solve the problem, without seeing why this problem occurred. It’s pretty much one step at a time. Heart good tired.