Write an ADHD resume using native JS
Preview address source address
Recently, I saw that @Fangyinghang wrote a moving resume with VUE on Zhihu. I thought it was quite fun, so I studied its implementation ideas and decided to try using native JS to achieve it.
Dynamic resume implementation ideas
This moving resume is like a typist constantly typing words, and the page appears to move. It’s like a movie has already been recorded and we’re just sitting in front of the projector watching it.
The principle has two parts
- The page sees the pulsing increment of text, controlled by innerHTML
- The layout effect of the page is changed by the hidden “behind”
style
Label to complete
Imagine you want to add one page to a web page every 0.1 secondah
Start a timer and plug it into the body intermittentlyah
, can ah! Yes, that completes the first part of the principle
Now imagine stuffing pagesah
When I want to change the font color of the word and the background color of the web page, how should I do that? Is it ok to execute the following code? Yes, only change the font and background color is not a sudden change, but is also a timer, intermittent tostyle
Insert the following code into the tag, which completes the second step of the principle, isn’t it easyLet’s do it step by step
.xxx{
color: blue;
background: red;
}
Copy the code
In this project we
- Use WebPack2 to complete the project construction
- Use YARN to manage dependency packages
- I’m going to use es6 notation
- Use part of the native DOM manipulation API
- Standard.js (Code style constraint tool)
The directory structure is as follows
The most important modules are resumeEditor(resumeEditor module), stylesEditor(resume style editor module), and vQuery(encapsulated dom manipulation module). Finally, app.js(entry module) combines the functions of several modules to complete the whole project.
VQuery (encapsulated DOM manipulation module)
Since the rest of the modules depend on this small module, let’s take a quick look at it first.
class Vquery {
constructor (selector.context) {
this.elements = getEles(selector, context)
}
optimizeCb (callback) {
.
}
get (index) {
.
}
html (sHtml) {
.
}
addClass (iClass) {
.
}
css (styles) {
.
}
height (h) {
.
}
scrollTop (top) {
.}}export default (selector.context) = > {
return new Vquery(selector, context)
}
Copy the code
As you can see, what it does is encapsulate a constructor, Vquery, whose instance will have some simple DOM manipulation methods. Finally, in order to be able to use $().funcname like jQuery, we export an anonymous function, in which we go to new Vquery
The layout of the resume is done by this module. The core method is showStyles.
const showStyles = (num.callback) = > {
let style = styles[num]
let length
let prevLength
if (!style) {
return
}
length = styles.filter((item.i) = > { //Computes the length of the first n elements of array styles
return i < = num
}).reduce((result.item) = > {
result + = item.length
return result
}, 0)
prevLength = length - style.length
clearInterval(timer)
timer = setInterval(() = > {
let start = currentStyle.length - prevLength
let char = style.substring(start, start + 1) || ''
currentStyle + = char
if (currentStyle.length = = = length) { //Styles has all the first n elements stuffed in, closes the timer, executes the callback passed in, and proceeds to the next operation
clearInterval(timer)
callback && callback()}else {
let top = $stylePre.height(a)- MAX_HEIGHT
if (top > 0) { //When the content has exceeded the height of the container, we need to set the scrolling distance to illustrate the following content
goBottom(top)
}
$style.html(currentStyle)
$stylePre.html(Prism.highlight(currentStyle, Prism.languages.css))
}
}, delay)
}
Copy the code
StylesEditor (stylesheet)
The resume editing module is used to display the content of the resume, mainly through the conversion from markdown format to HTML page form.
const markdownToHtml = (callback) => { $resumeMarkdown.css({ display: 'None'}) $resumewrap.addClass (iClass) $resumetag.html(marked(resumeMarkdown) Callback () // perform subsequent callback} const showResume = (callback) => { Turn off the timer when it's all plugged in, ClearInterval (timer) timer = setInterval(() => {currentMarkdown += resumemarkDown. substring(start, start + 1) if (currentMarkdown.length === length) { clearInterval(timer) callback && callback() } else { $resumeMarkdown.html(currentMarkdown) start++ } }, delay) }
Copy the code
Finally, the app entry module integrated the above modules to complete the function of the project. We found out the core code.You read that right, the legendary callback hell, which is so stupid. You probably don’t want to see this nasty piece of code as much as I do, but callbacks have always been a solution to asynchrony problems.
Because the operation of the timer is asynchronous behavior, and we will resume the generation process involves multiple asynchronous operations, so in order to see effect, such as home page preview links must wait for the previous step is completed, to perform the next step, the first use of the solution to the callback function, you can pull out the code, Switch the following branches to see the different solutions
- Master (handled using callback functions)
- Promises (handle with promises)
- Generator-thunk (handle with generator + thunk function)
- Generator-promise (handle with generator + promise)
- Async (using async processing)
showStyles(0, () = > {
showResume(() = > {
showStyles(1, () = > {
markdownToHtml(() = > {
showStyles(2)})})})})Copy the code
Resolve the promise of callback hell
The callback method can solve the problem of asynchronous operation, but the code is very ugly to write, poor readability, the code is horizontal development trend… Great programmers spread the boundaries and invented Promise’s solution. Let’s take a look at how the app module in the Promise branch ends up
showStylesWrap(0).then(showResumeWrap)
.then(showStylesWrap.bind(null.1)).then(markdownToHtmlWrap)
.then(showStylesWrap.bind(null.2))
Copy the code
As you can see, the code is much cleaner, vertically, applying step 1, Step 2, step 3… At a glance, you can see that, of course, the implementation logic is to wrap the original related module with Promise, and resolve in the original callback function execution place, detailed implementation, welcome to check the project source code
Both approaches are similar in that they use generators in ES6. For information on what generator and thunk functions are, see the Software wizard’s Introduction to ECMAScript 6, which briefly describes how it handles asynchronous operations so that asynchronous behavior can be written as if it were synchronous.
function timeOut1 () {
setTimeout(() = > {
console.log(1111)
g.next()},1000)}function timeOut2 () {
setTimeout(() = > {
console.log(2222)},200)}function * gen () {
yield timeOut1(a)yield timeOut2()}let g = gen(a)g.next(a)Copy the code
The code above logs 2222 after 200 milliseconds and 1111 after 1 second
This,TimeOut2 is not executed after timeOut1, as the gen function seems to want it to be.
No, timeOut2 execution depends on when
g.next(a)g.next(a)Copy the code
If two functions are executed almost at the same time, then in the timer, of course, timeOut2 will print 2222 after 200ms, but is there a way to make timeOut2 execute after timeOut1? The answer is yes
function timeOut1 () {
setTimeout(() = > {
console.log(1111)},1000)}function timeOut2 () {
setTimeout(() = > {
console.log(2222)},200)}function * gen () {
yield timeOut1(a)yield timeOut2()}let g = gen(a)g.next(a)g.next(a)Copy the code
As you can see, after timeOut1 is executed, we point to the next location, timeOut2, and the result is the same as if the two yields in the gen function were written in sync. One problem is that it is difficult to manage asynchronous processes in this way if many asynchronous operations are involved. So here’s what we need to do
function co (fn) {
var gen = fn(a);function next(err.data) {
var result = gen.next(data);
if (result.done) return;
result.value(next); //One of the differences between thunk and promise is here, promise is result.value.then(next)
}
next(a); }Copy the code
The internal next function is the thunk callback. The next function moves the pointer to the next step in the generator function (the Gen. next method) and then determines whether the generator function is finished (the result.done property). If not, Pass next to thunk (result.value), or exit.
Finally, let’s look at writing the co function to complete the above example
function timeOut1() {
return (callback) = > {
setTimeout(() = > {
console.log(1111)
callback()},1000)}}function timeOut2() {
return (callback) = > {
setTimeout(() = > {
console.log(2222)
callback()},200)}}function co(fn) {
var gen = fn(a);function next(err.data) {
var result = gen.next(data);
if (result.done) return;
result.value(next); //One of the differences between thunk and promise is here, promise is result.value.then(next)
}
next(a); }co(function * () {
yield timeOut1(a)yield timeOut2()})Copy the code
Resolve async in callback hell
Async is a syntactic sugar for generator functions. If you understand generator, you will not be able to use it. For details on the use of this project, please refer to the async branch source code.
There may be some improper explanations in this article, please correct them. Finally, click “like” and “star”.The source address