I wrote one earlierCalendar – Custom Calendar “made for mobile”There have been some problems with the gesture processing of this plugin, so I want to write an article about its growth history

Before reading this article, make sure you have looked at the Calendar a bit

  • Click on Github to view the calendar source

  • It can also be found by searching moon-Calendar on NPM.

1. Identify requirements

The main reason to want to make a calendar, of course, is because of frequent encounters during development. What’s more, the demand for calendar is so weird that plug-ins on the market can’t meet the needs of our products. So I had to build my own.

This paragraph, as if in the last plug-in – cascade selector also said that everyone as nothing happened (⁎⁍̴̛ᴗ⁍̴̛⁎)

The first problem remains dealing with demand:

Question 1: “What are the characteristics of the scene in which the calendar appears?”

  1. The user is not sure about the time point or time range to choose, and needs some basic reference units of time, such as “next Monday” or “next weekend”.

  2. Users need to check a certain time range, and then select a time point or time range, such as “try to avoid the 20-day weekend miss plan”.

  3. Users need to view behavior records for a certain time period, such as “View clocking in the past few weeks.”

The timing advantage of the calendar comes into play when these problems occur.

Question 2: “What are the weird needs of the calendar?”

  1. Calendar has click event, click event is jump event or highlight event can not predict.

  2. Calendar exists selection operation, the selection of the result is not predictable time point or time range.

  3. Calendars can be presented in a variety of ways, and it is unpredictable whether they will be presented as a direct document stream or as a pop-up.

For these unstable factors, the following will take you step by step.

Second, the constructor parameter design

Having identified the calendar requirements, let’s design the constructor parameters

Question 3: “What are the common representations of the calendar?”

From the common apps on the market, we can find that calendars are commonly displayed in two ways:

  1. Normal document flow form

  2. Elastic layer form

In parameter Settings, isMask is set as follows: false: common; true: shell.



Question 4: “How can parameters be set flexibly and efficiently?”

1. Make it easier for developers to locate dates

① : When determining the time range, use an array with length 3, each bit of the array corresponds to [year] [month] [day], such as beginTime, endTime and recentTime Settings

② : When specifying a style or operation for a particular date, use the timestamp of that date.

Such as setting upbeforeRenderArrYou need to pass in an array of objects that conform to the specification

parameter type For example, instructions
stamp {Number} eg:1514822400000 Specify a specific timestamp
className {String} eg: “enable” Specify a user-specified CSS class name

2. Flexible control of week arrangement, week display format, month display format

IsSundayFirst controls whether Sunday should be placed in the first column, true indicates Sunday should be placed in the first column

② : isChinese controls the display mode of the week. True indicates that Chinese is displayed, false indicates that English is displayed

3: monthType Controls the display format of a month. Take January as an example. 0: January, 1: January, 2:Jan, 3: January

3. Configure the most important swipe gestures

① : Angle controls the sliding Angle and indirectly controls the sensitivity. The recommended value range is 5-20

② : isToggleBtn whether the switch button needs to be displayed, true indicates that it needs to be displayed

③ : canViewDisabled Indicates whether to query the month that is not in the specified range. True indicates that the month can be queried

4. Flexible callback functions for developers to customize

① : SUCCESS click the callback after a certain date, the user can customize the operation after the click. Built-in argument (item, ARR). Item is the time stamp of the current click, and ARR is the array of two time stamps of two consecutive clicks after intelligent judgment

② : switchRender Is the callback of the month when the switch is performed. Users can customize the operation after the switch, such as initiating a request to update data. Built-in parameters (year, month, CAL). Year is the new generated year, month is the new generated month (starting from 0), and CAL points to the current instance

3. Exposed, usable apis on the prototype

The name of the The type of the parameter passed in role
renderCallbackArr(arr) {Array} Render the specified ARR, arR format andbeforeRenderArrThe format of the object array is the same
prevent() In the wechat browser, you may need to use the API to block default events
hideBackground() In shell modesuccessYou may need to use the API to close the shell layer in callbacks

To properly explain the purpose of the API:

1. Pass an array to renderCallbackArr (the array format is the same as beforeRenderArr, which is not specified). This method will add styles to the point in time you need them. Consider a scenario:

Swipe to see how clocked up three months ago, with both clocked and unclocked dates highlighted differently.

Obviously, this month’s clock-in situation requires your presenceswitchRenderAfter an HTTP request is made in the callback.

After HTTP returns the result, construct a matchbeforeRenderArrFormat the array and then callrenderCallbackArr, passing in the constructed array renders the specified className for the specified date.



// For example, 🌰
switchRender:  function(year, month,cal) {
    console.log('Computer recognized: Year:' + year + 'Month:' + month);
    $.ajax({
        url: 'xxxx'.type: 'GET'.data: {
            applyYear: year,
           applyMonth: (month + 1),},success: function(newArr) { cal.renderCallbackArr(newArr); }})}Copy the code

2. There shouldn’t be too many scenarios using prevent(). Mainly to prevent wechat browser from default sliding.



// This is the source of prevent method
prevent: function (e) {
      e.preventDefault();
},Copy the code

3. Scenarios using hideBackground() are usually in the success callback of shell mode. Consider a scenario:

If you only want to “select a point in time” after the calendar layer is triggered, you can call it directly after clicking on a datehideBackground()Retract the ammo.

If you want to [select a time range], you can call it after the second time point is determinedhideBackground()Retract the ammo. Of course, you can also leave the shell unpacked.

How to use five DOM to achieve infinite sliding

In fact, when I wrote the first version of the calendar, THE solution I took was to append the DOM into the body as the new month was generated. But at that time, the business scene is relatively simple, only 10 months to support the death. But obviously with 100 months, it’s not going to work.

So you have to make the DOM reusable, sliding indefinitely

Consider question 5: “How many dom does it take to slide infinitely?”

To be clear, a DOM referred to here is a month, and every time you switch a month you switch the DOM that wraps the month

As shown below, assume that the current month is [September 2017]. Since the sliding is real-time, when MY finger slides from right to left, [October 2017] will also be gradually exposed. Consider a special case:

Take punching, for example, there is punching record in October 2017. If the user releases his finger and stops in October 2017, the highlighted pattern of punching record suddenly flashes, which will give the user a very uncomfortable feeling.

To avoid this, it is necessary to render the highlighting of October 2017 when the current month is September 2017, as well as August 2017 on the left, so at least the full three months with data highlighting must be rendered

So we conclude that there are at least three dom months, and that the three DOM’s are already rendered with the highlighting style, and will not change after the real-time slide ends.

But why did you end up with five DOM’s for infinite sliding?

Take a look at swiper, so that the extreme DOM on each side of the three DOM can also be swiped in real time. So we add one DOM at the beginning and one dom at the end, so we need five DOM to slide indefinitely.

As shown in the figure below, the part of the green wire box is the initial analysis of the three DOM.



Consider question 6: “Which month should the first and last padded DOM be displayed?”

A quick look at swiper will give you the answer. Here’s an example: Consider the following:

Gesture operation: continuously from right to left operation result: continuously view next month

Here’s an example of the red arrow’s update operation:

Take September 2017 as an example:

Initial state:



The purple number is the subscript of the month DOM, and the same subscript corresponds to the same month. The ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle, the ones in the middle. So why are the first and last filled months 3 and 1?

Assuming that instead of limiting five DOM to an infinite number of DOM, the subscript combinations representing the month DOM would be: 1, 2, 3, 1, 2, 3, 1, 2, 3……

We take a 1, 2, and 3 as the center to get 5 consecutive months dom, then the subscript combination obtained is: 1, 2, [3, 1, 2, 3], 2, 3, 1, 2, 3……

Didn’t understand it doesn’t matter, look down will understand.

Consider question 7: “How do I control the months that are displayed in order to work with infinite sliding?”

In fact, in the future, I’ll need to fetch the dom index to update the month data, so I’m trying to find a pattern in the [3, 1, 2, 3, 1] index array.

I realized that this subscript loop is a loop of 3, and I can get the DOM subscripts at each position by taking the modulo of 3.

Now I’m going to make a little change to this subscript. I’m going to change the 3 to 0. Namely [0, 1, 2, 0, 1]

The reason is simply to make it easier to map the DOM subscript to translateX when calculating the sliding distance. That is, when you slide to the left most month DOM, the value of translateX of the month dom is 0, which can correspond to the result of subscript 0%3.

In this case, the subscript is equal totranslateXIt’s a direct connection.

Ok, take September 2017 as the initial month, and the final initialization result is:



Next, swipe from right to left to see the next month, and after Touchend, do the following:

When you slide into the dom of the rightmost month (You do the same thing whenever you go to the boundary),touchstartTo perform a special operation:

That’s when you touchstart, all of a suddentranslate3dGo to the same month as its DOM subscript:

For example, the one above [2017.11] has gone to the far right, which will be slid next timetouchstartTo the position shown below:



This is the core principle of infinite sliding. Of course, you can go on and on:





And so on,Infinite left swipe is similar.

Why is it necessary to anticipate user gestures

The slider distance is determined by controlling the center gray rectangle relative to the translateX of the phone screen.

How to control the value of translateX to achieve the sliding effect is not the focus of this time.

Importantly, think about question 8: “What if I only control translateX in the DOM area of the calendar, and when I try to slide the entire page, it doesn’t work?”

Assume that the blue curve in the figure below represents the user’s sliding curve:

When the user’s slide curve is A, the user’s intention is obviously to pull the page up and when the user’s slide curve is B, the user’s intention is obviously to look at the last month

But in fact, if you just controltranslateXWhen the sliding effect is implemented, either curves A or B are considered to be looking at the previous month

 

That is, if you control translateX, you will never be able to slide up or down in the DOM, which occupies a large area of the document flow. This should never be allowed.

So we need to anticipate gestures in order to implement, within the DOM of the calendar,It can slide up and down as well as side to side. The effect is as follows:

Consider question 9: “Is there an easy way to predict gestures?”

For example, in the example of sliding curves A and B mentioned earlier, if the green line is used as the standard,

  1. Any curve with a slope less than the green line is classified as sliding left and right as sliding curve B

  2. If the slope is greater than the green line, it’s going to slide up and down the same way as sliding curve A

Isn’t that enough? But in fact, the user’s gesture curve is generally the orange curve below….

  

And calculating the slope of a user’s gesture must be done in real time in TouchMove (why? For real-time swiping, of course), so the idea of predicting user gestures by slope ends there.

How to use calculus to predict user gestures

Consider question 10: “Does integrating user gestures solve the problem?”

The user’s gesture is actually an arc, and the current slide from the lower left corner to the upper right corner can simplify the user’s gesture curve in the first quadrant. In the figure below, we start from the concept of calculus and get the following conclusions.

   

Take a look at the red rectangle in the middle. The red rectangle is an exaggerated version of a long rectangle with a width of △X and a height of △Y. Calculate △X and △Y of each slide in real time through TouchMove, and then add up the area. The sum of the areas actually adds directly to the sum of the results of △X × △Y, thus extending the gestures in the first quadrant to all the gestures in the quadrants.

The core code for calculating the gesture is as follows, where CAL points to the current instance:



   

We can use the curve area of user gesture to quantify user gesture operation. But quantization is quantization, how do I know if I’m quantizing up and down or left and right? So you have to have a standard area just like the standard line, the green line, when you calculate the slope.

Consider question 10: “How to calculate the standard area?”

In the figure below, we have three curves, and the area enclosed by these three curves and the X-axis is the result of our painstaking quantification. Among them:

The area of the blue curve is our ideal standard area, although we don’t know how to calculate it, right

The area enclosed by the yellow curve is larger than the standard area. We will judge all quantized curves larger than the blue curve as [user tries to slide up and down].

The area enclosed by the green curve is smaller than the standard area. We will judge that all quantized curves smaller than the blue curve are [the user tries to slide left and right].

 

So the question comes back to, how do you calculate the standard area? If you look at the figure above, you can see that there is A distinct blue Angle A, which is the same thing as the instantiated argument Angle.

Developers can control the size of the standard area by controlling the value of Angle (which is in degrees). Of course, through my test, the value of Angle is best in [5, 20].

How is the source code passed in by the developer Angle to calculate the standard area?

First of all, I’m going to convert the user’s Angle to the tan value.



 

Why do I need the tangent of theta, because THEN I can say, okayDelta XTo calculate theDelta Y = delta X * tanA.



 

So the standard area can also be added up.



 

So far, we can get an ideal prediction by comparing the area of the user’s gesture with the standard area. By anticipating, users feel comfortable swiping anywhere on the page.

conclusion

Making address: “born for mobile terminal” custom calendar plugin https://github.com/AppianZ/calendar

٩(•̤̀ᵕ•̤́๑)

I am E Garbo Appian, e e e e e e algorithm girl (E ᴗ͈ˬᴗ͈)