成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

前端小項(xiàng)目之在線便利貼

microelec / 1782人閱讀

摘要:實(shí)現(xiàn)的效果如下界面可能不是太好看,考慮到容器的高度會(huì)被拉長(zhǎng),因此沒(méi)有用圖片做背景。

實(shí)現(xiàn)的效果如下:

界面可能不是太好看?,考慮到容器的高度會(huì)被拉長(zhǎng),因此沒(méi)有用圖片做背景。

預(yù)覽

便利貼

涉及的知識(shí)點(diǎn)

sass(css 預(yù)編譯器)

webpack(自動(dòng)化構(gòu)建工具,實(shí)現(xiàn)LESS,CSS,JS編譯和壓縮代碼)

express (基于 Node.js 平臺(tái)的 web 開(kāi)發(fā)框架)

html+css

Node.js(基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境)

jQuery(一個(gè)快速、簡(jiǎn)潔的JavaScript框架)

sequelize(Node的ORM框架Sequelize操作數(shù)據(jù)庫(kù))

passport(實(shí)現(xiàn)第三方登錄)

實(shí)現(xiàn)功能

github第三方登錄

添加筆記(登錄成功后)

刪除筆記

修改筆記

使用 markdown(類(lèi)似 typroa)

筆記拖拽

準(zhǔn)備工作

必要條件:已經(jīng)安裝好了node環(huán)境,還沒(méi)安裝的可以去node中文官網(wǎng)下載

小提示:如果用 npm 下載感覺(jué)慢的話,可以下載一個(gè)切換鏡像源的工具nrm,在終端輸入:

npm i nrm -g

然后如下操作:

開(kāi)始!!

1.新建一個(gè)文件夾,名字自己起,打開(kāi)終端,切換到自己新建文件夾,如

cd (文件夾名稱)

2.生成 package.json

npm init -y

3.安裝 express

npm i express --save

4.安裝 express生成器:

npm install express-generator --save

5.生成 ejs 模板(類(lèi)似 jsp 的寫(xiě)法)

express -f -e
npm i

其中public用來(lái)存放編譯后的js文件以及編譯好的css文件等,routes用來(lái)存放處理 ajax 的請(qǐng)求文件,views就是存放視圖文件
然后新建 database 和 src:

其中 src/js 里面 app 代表不同頁(yè)面的入口文件,lib 就是一些常用的庫(kù),mod 就是你寫(xiě)的一些模塊,database 用來(lái)存放數(shù)據(jù)庫(kù)數(shù)據(jù)的

6.輸入:

npm start

如果有出現(xiàn)下面的錯(cuò)誤:

出現(xiàn)這個(gè)錯(cuò)誤是因?yàn)槟銢](méi)有下載模塊,只需在終端輸入:

npm i (模塊名) --save

就可以了

7.打開(kāi)瀏覽器,輸入localhost:3000
出現(xiàn)下面這樣就說(shuō)明成功了:

8.接下來(lái)安裝webpack和相關(guān)依賴

npm i webpack --save-dev
npm i --save css-loader style-loader express-session express-flash node-sass passport sass sass-loader sequelize sqlite3 extract-text-webpack-plugin onchange

9.在 src 里建一個(gè) webpack.config.js,配置如下

var webpack = require("webpack");
var path = require("path");
var ExtractTextPlugin = require("extract-text-webpack-plugin")
var autoprefixer = require("autoprefixer");
    
module.exports = {
    entry: path.join(__dirname, "js/app/index"),
    output: {
        path: path.join(__dirname, "../public"),
        filename: "js/index.js"
    },
    module: {
        rules: [{
            test: /(.scss)$/,
            use: ExtractTextPlugin.extract({
                fallback: "style-loader",
                use: ["css-loader", "sass-loader"]
            }) //把 css 抽離出來(lái)生成一個(gè)文件
        }]
    },
    resolve: {
        alias: {
            jquery: path.join(__dirname, "js/lib/jquery-2.0.3.min.js"),
            mod: path.join(__dirname, "js/mod"),
            sass: path.join(__dirname, "sass")
        }
    },
    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery"
        }),
        new ExtractTextPlugin("css/index.css"),
        new webpack.LoaderOptionsPlugin({
            options: {
                css: [
                    autoprefixer(),
                ]
            }
        }),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
}

說(shuō)明

entry:入口文件,也就是 src/js/app里面的index.js,其中__dirname是獲得當(dāng)前文件所在目錄的完整目錄名

output:輸出編譯后的文件 index.js,輸出到 public/js 里面

module:配置Loaders,通過(guò)使用不同的loader,webpack有能力調(diào)用外部的腳本或工具,實(shí)現(xiàn)對(duì)不同格式的文件的處理,比如說(shuō)分析轉(zhuǎn)換scss為css,或者把下一代的JS文件

resolve.alias:設(shè)置模塊別名,便于我們更方便引用,比如說(shuō)我在 js里面的文件需要 jquery,在里面的文件直接寫(xiě) require("jquery") 就行了

如果所有文件都需要 jquery,那么直接在 plugins里面寫(xiě)成這樣:

就不需要 require 了


這個(gè)是壓縮文件的

10.在 package.json 中,增加如下兩條:

寫(xiě)成這樣,你在終端就可以寫(xiě)成npm run webpack 來(lái)編譯文件,
npm run watch來(lái)監(jiān)控 src 里面的 js 和 scss 的變化,只要一修改,進(jìn)行編譯,提高了效率

11.測(cè)試

你可以試試在 js 里面的 index.js寫(xiě)點(diǎn)東西,然后 npm run webpack,如果終端顯示是這樣:

就證明成功了

項(xiàng)目思路

邏輯比較簡(jiǎn)單

首先用戶必須登錄才能添加筆記,當(dāng)用戶失焦的時(shí)候,將數(shù)據(jù)插入數(shù)據(jù)庫(kù),并且重新布局(瀑布流)

用戶不能更改其他用戶的筆記,除了管理員?

用戶更新筆記之后,數(shù)據(jù)庫(kù)的數(shù)據(jù)重新更新,重新布局

用戶可以刪除筆記,數(shù)據(jù)從數(shù)據(jù)庫(kù)中刪除,重新布局

用戶可以拖拽筆記,但不將位置存入數(shù)據(jù)庫(kù)

實(shí)現(xiàn)

html,css就不講了,可以看看我的源碼,主要講 js。

1.瀑布流的實(shí)現(xiàn)

思路:(前提是必須絕對(duì)定位)

獲取元素的寬度

通過(guò)窗口的寬度除以元素的寬度來(lái)獲取列數(shù)

初始化一個(gè)數(shù)組來(lái)獲取每列的高度,初始化每列的高度為0

遍歷元素,獲取最小的的列數(shù)的高度和索引,對(duì)當(dāng)前元素進(jìn)行定位,列數(shù)高度加等于當(dāng)前元素的高度

知道思路后,代碼很快就寫(xiě)出來(lái)了:

var WaterFall = (function () {
        var $ct, $items;
        function render($c) {
            $ct = $c;
            $items = $ct.children();
            var nodeWidth = $items.outerWidth(true),
                windowHeight = $(window).height(),
                colNum = parseInt($(window).width() / nodeWidth), //獲取列數(shù)
                colSumHeight = []; //獲取每列的高度
            //對(duì)每列的高度進(jìn)行初始化
            for (var i = 0; i < colNum; i++) {
                colSumHeight[i] = 0;
            }
            $items.each(function () {
                var $current = $(this);
                var index = 0,
                    minSumHeight = colSumHeight[0];
                //獲取最小的的列數(shù)的高度和索引
                for (var i = 0; i < colSumHeight.length; i++) {
                    if (minSumHeight > colSumHeight[i]) {
                        index = i;
                        minSumHeight = colSumHeight[i];
                    }
                }            
                //改變窗口高度
                if (windowHeight < minSumHeight) {
                    $("body").height(minSumHeight);
                } else {
                    $("body").height(windowHeight - 72);
                }
                //對(duì)當(dāng)前元素進(jìn)行定位
                $current.animate({
                    left: nodeWidth * index,
                    top: minSumHeight
                }, 5);
                colSumHeight[index] += $current.outerHeight(true);
    
            });
        }
        //當(dāng)窗口發(fā)生變化時(shí),重新渲染
        $(window).on("resize", function () {
            render($ct);
        });
        return {
            init: render
        }
    })();

2.筆記的拖拽

我們先看個(gè)圖

因此代碼如下:

      //設(shè)置筆記的移動(dòng)
            $noteHead.on("mousedown", function (e) {
                var evtX = e.pageX - $note.offset().left, //evtX 計(jì)算事件的觸發(fā)點(diǎn)在 dialog內(nèi)部到 dialog 的左邊緣的距離
                    evtY = e.pageY - $note.offset().top;
                $note.addClass("draggable").data("evtPos", {
                    x: evtX,
                    y: evtY
                }); //把事件到 dialog 邊緣的距離保存下來(lái)
            }).on("mouseup", function () {
                $note.removeClass("draggable").removeData("pos");
            });
    
            $("body").on("mousemove", function (e) {
                $(".draggable").length && $(".draggable").offset({
                    top: e.pageY - $(".draggable").data("evtPos").y, // 當(dāng)用戶鼠標(biāo)移動(dòng)時(shí),根據(jù)鼠標(biāo)的位置和前面保存的距離,計(jì)算 dialog 的絕對(duì)位置
                    left: e.pageX - $(".draggable").data("evtPos").x
                });
            });
        },

3.提示模塊

這個(gè)比較容易:

    /* 
    提示模塊
    參數(shù):狀態(tài)(1表示成功,0表示失敗),消息,出現(xiàn)時(shí)間(不寫(xiě)默認(rèn)是1s)
     */
    function toast(status, msg, time) {
        this.status = status;
        this.msg = msg;
        this.time = time || 1000;
        this.createToast();
        this.showToast();
    }
    
    toast.prototype = {
        createToast: function () {
            if (this.status === 1) {
                var html = "
![](../../imgs/1.png)" + this.msg + "
"; this.$toast = $(html); $("body").append(this.$toast); } else { var html = "
![](../../imgs/0.png)" + this.msg + "
"; this.$toast = $(html); $("body").append(this.$toast); } }, showToast: function () { var _this = this; this.$toast.fadeIn(300, function () { setTimeout(function () { _this.$toast.fadeOut(300, function () { _this.$toast.remove(); }); }, _this.time); }) } } function Toast(status, msg, time) { return new toast(status, msg, time); }

4.筆記模塊

思路:

初始化(如 id,username 等等)

創(chuàng)建節(jié)點(diǎn)

設(shè)置顏色

綁定事件

function Note(opts) {
    this.initOpts(opts);
    this.createNode();
    this.setColor();
    this.bind();
}

Note.prototype = {
    colors: [
        ["#ea9b35", "#efb04e"], // headColor, containerColor
        ["#dd598b", "#e672a2"],
        ["#c24226", "#d15a39"],
        ["#c1c341", "#d0d25c"],
        ["#3f78c3", "#5591d2"]
    ],
    defaultOpts: {
        id: "", //Note的 id
        $ct: $("#content").length > 0 ? $("#content") : $("body"), //默認(rèn)存放 Note 的容器
        context: "請(qǐng)輸入內(nèi)容", //Note 的內(nèi)容
        createTime: new Date().toLocaleDateString().replace(///g, "-").match(/^d{4}-d{1,2}-d{1,2}/),
        username: "admin"
    },
    initOpts: function (opts) {
        this.opts = $.extend({}, this.defaultOpts, opts || {});
        if (this.opts.id) {
            this.id = this.opts.id;
        }
        this.createTime = this.opts.createTime ? this.opts.createTime : new Date().toLocaleDateString().replace(///g, "-").match(/^d{4}-d{1,2}-d{1,2}/);
        this.username = this.opts.username ? this.opts.username : "admin"
    },
    createNode: function () {
        var tpl = "
" + "
×
" + "
" + "
" + this.username + "
" + this.createTime + "
" + "
"; this.$note = $(tpl); this.$note.find(".note-ct").html(this.opts.context); this.opts.$ct.append(this.$note); //if (!this.id) this.$note.css("bottom", "10px"); //新增放到右邊 Event.fire("waterfall"); }, setColor: function () { var color = this.colors[Math.floor(Math.random() * 5)]; this.$note.find(".note-head").css("background-color", color[0]); this.$note.find(".note-ct").css("background-color", color[1]); this.$note.find(".note-info").css("background-color", color[1]); }, setLayout: function () { var self = this; if (self.clock) { clearTimeout(self.clock); } self.clock = setTimeout(function () { Event.fire("waterfall"); }, 100); }, bind: function () { var _this = this, //記錄下坑,之前末尾是分號(hào)不是逗號(hào)后面都變成了全局變量結(jié)果造成了最后一個(gè)才能修改? $note = this.$note, $noteHead = $note.find(".note-head"), $noteCt = $note.find(".note-ct"), $close = $note.find(".delete"); $close.on("click", function () { _this.delete(); }); $noteCt.on("focus", function () { if ($noteCt.html() === "請(qǐng)輸入內(nèi)容") $noteCt.html(""); $noteCt.data("before", $noteCt.html()); }).on("blur paste", function () { if ($noteCt.data("before") != $noteCt.html()) { $noteCt.data("before", $noteCt.html()); _this.setLayout(); if (_this.id) { //判斷是否有這個(gè)id,如果有就更新,如果沒(méi)有就添加 _this.edit($noteCt.html()) } else { _this.add($noteCt.html()) } } }); //設(shè)置筆記的移動(dòng) $noteHead.on("mousedown", function (e) { var evtX = e.pageX - $note.offset().left, //evtX 計(jì)算事件的觸發(fā)點(diǎn)在 dialog內(nèi)部到 dialog 的左邊緣的距離 evtY = e.pageY - $note.offset().top; $note.addClass("draggable").data("evtPos", { x: evtX, y: evtY }); //把事件到 dialog 邊緣的距離保存下來(lái) }).on("mouseup", function () { $note.removeClass("draggable").removeData("pos"); }); $("body").on("mousemove", function (e) { $(".draggable").length && $(".draggable").offset({ top: e.pageY - $(".draggable").data("evtPos").y, // 當(dāng)用戶鼠標(biāo)移動(dòng)時(shí),根據(jù)鼠標(biāo)的位置和前面保存的距離,計(jì)算 dialog 的絕對(duì)位置 left: e.pageX - $(".draggable").data("evtPos").x }); }); }, /* 添加筆記到數(shù)據(jù)庫(kù) */ add: function (msg) { var _this = this; $.post("/api/notes/add", { note: msg }).done(function (res) { if (res.status === 1) { _this.id = res.id; Toast(1, "添加成功!"); } else { _this.$note.remove(); Event.fire("waterfall"); Toast(0, res.errorMsg); } }) }, /* 編輯筆記數(shù)據(jù)庫(kù) */ edit: function (msg) { var _this = this; $.post("/api/notes/edit", { id: this.id, note: msg }).done(function (res) { if (res.status === 1) { Toast(1, "更新成功!"); } else { Toast(0, res.errorMsg); } }); }, /* 刪除筆記 */ delete: function () { var _this = this; if (confirm("確認(rèn)要?jiǎng)h除嗎?")) { $.post("/api/notes/delete", { id: this.id }).done(function (res) { if (res.status === 1) { Toast(1, "刪除成功!"); _this.$note.remove(); Event.fire("waterfall") } else { Toast(0, res.errorMsg); } }); } } }

5.筆記管理模塊

var NoteManager = (function () {
    //頁(yè)面加載
    function load() {
        $.get("api/notes").done(function (res) {
            if (res.status === 1) {
                $.each(res.data, function (index, msg) {
                    new Note({
                        id: msg.id,
                        context: msg.text,
                        createTime: msg.createdAt.match(/^d{4}-d{1,2}-d{1,2}/),
                        username: msg.username
                    });
                });

                Event.fire("waterfall");

            } else {
                Toast(0, res.errorMsg);
            }
        }).fail(function () {
            Toast(0, "網(wǎng)絡(luò)異常");
        });
    }

    /* 添加筆記 */
    function add() {
        $.get("/login").then(function (res) {//判斷是否登錄
            if (res.status === 1) {
                new Note({
                    username: res.username
                });
            } else {
                Toast(0, res.errorMsg);
            }
        });
    }
    return {
        load: load,
        add: add
    }
})();

6.發(fā)布訂閱模式

/* 發(fā)布訂閱模式 */
var Event = (function () {
    var events = {};

    function on(evt, handler) {
        events[evt] = events[evt] || [];
        events[evt].push({
            handler: handler
        });
    }

    function fire(evt, args) {
        if (!events[evt]) {
            return;
        }
        for (var i = 0; i < events[evt].length; i++) {
            events[evt][i].handler(args);
        }
    }

    function off(name) {
        delete events[name];
    }
    return {
        on: on,
        fire: fire,
        off: off
    }
})();

寫(xiě)完模塊后,寫(xiě)入口文件index.js

require("sass/index.scss");
var Toast = require("mod/toast.js").Toast;
var WaterFall = require("mod/waterfall.js");
var NoteManager = require("mod/note-manager");
var Event = require("mod/event.js");


NoteManager.load();
$(".add-note").on("click", function () {
    NoteManager.add();
})

Event.on("waterfall", function () {
    WaterFall.init($("#content"));
})

到這就差不多完成了70%了,接下來(lái)就創(chuàng)建數(shù)據(jù)庫(kù),連接數(shù)據(jù)庫(kù)了

/*創(chuàng)建數(shù)據(jù)庫(kù) 運(yùn)行 node note.js*/

var Sequelize = require("sequelize");
var path = require("path");

var sequelize = new Sequelize(undefined, undefined, undefined, {
    host: "localhost",
    dialect: "sqlite",
    // SQLite only
    storage: path.join(__dirname, "../database/database.sqlite")
});

/* 測(cè)試連接是否成功
node note.js

sequelize.authenticate()
    .then(() => {
        console.log("Connection has been established successfully.");
    })
    .catch(err => {
        console.error("Unable to connect to the database:", err);
    });

*/


var Note = sequelize.define("note", {
    text: {
        type: Sequelize.STRING
    },
    userid: {
        type: Sequelize.INTEGER
    },
    username: {
        type: Sequelize.STRING
    }
});

Note.sync();

/*
刪除表
Note.drop();
*/


/*
//創(chuàng)建數(shù)據(jù)庫(kù)

Note.sync().then(function(){
     Note.create({text:"sdsdsdsd"});
}).then(function(){
    //查詢表
    Note.findAll({raw:true}).then(function(notes){
        console.log(notes);
    })
});
*/




module.exports = Note;

然后是在routes 里處理 ajax 請(qǐng)求,處理登錄信息,獲取 id,用戶名等等,到這就基本完成了

總結(jié)

經(jīng)過(guò)一星期的開(kāi)發(fā),了解了前后端聯(lián)調(diào),模塊化開(kāi)發(fā)方式、webpack 及l(fā)oader和插件的使用、npm 的使用,Express的使用、路由、中間件、sqlite3、nodejs,在開(kāi)發(fā)過(guò)程中還是有遇到許多問(wèn)題,例如在連續(xù)聲明變量的時(shí)候,不小心把逗號(hào)寫(xiě)成了分號(hào),其他變量就變成了全局變量,于是就出錯(cuò)了,查了好久?

不過(guò)在這過(guò)程之中還是學(xué)到了許多,重要的是過(guò)程,繼續(xù)往前端的路走下去?

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/91788.html

相關(guān)文章

  • 2016年總結(jié) - 收藏集 - 掘金

    摘要:然而這次的文章,就像賀師俊所說(shuō)的這篇文章是從程序員這個(gè)老年度總結(jié)前端掘金年對(duì)我來(lái)說(shuō),是重要的一年。博客導(dǎo)讀總結(jié)個(gè)人感悟掘金此文著筆之時(shí),已經(jīng)在眼前了。今天,我就來(lái)整理一篇,我個(gè)人認(rèn)為的年對(duì)開(kāi)發(fā)有年終總結(jié)掘金又到 2016 Top 10 Android Library - 掘金 過(guò)去的 2016 年,開(kāi)源社區(qū)異?;钴S,很多個(gè)人與公司爭(zhēng)相開(kāi)源自己的項(xiàng)目,讓人眼花繚亂,然而有些項(xiàng)目只是曇花一...

    DataPipeline 評(píng)論0 收藏0
  • 前端閱讀 - 收藏集 - 掘金

    摘要:實(shí)現(xiàn)不定期更新技巧前端掘金技巧,偶爾更新。統(tǒng)一播放效果實(shí)現(xiàn)打字效果動(dòng)畫(huà)前端掘金前端開(kāi)源項(xiàng)目周報(bào)前端掘金由出品的前端開(kāi)源項(xiàng)目周報(bào)第四期來(lái)啦。 Web 推送技術(shù) - 掘金騰訊云技術(shù)社區(qū)-掘金主頁(yè)持續(xù)為大家呈現(xiàn)云計(jì)算技術(shù)文章,歡迎大家關(guān)注! 作者:villainthr 摘自 前端小吉米 伴隨著今年 Google I/O 大會(huì)的召開(kāi),一個(gè)很火的概念--Progressive Web Apps ...

    lingdududu 評(píng)論0 收藏0
  • 【Java】廣州三本秋招經(jīng)歷

    摘要:具體的時(shí)間線從月中旬,我開(kāi)始關(guān)注牛客網(wǎng)的秋招內(nèi)推信息。直至十月中下旬結(jié)束秋招。之前也寫(xiě)過(guò)自己在廣州找實(shí)習(xí)的經(jīng)歷,那次把面試的過(guò)程都具體貼出來(lái)了。我今年就完美錯(cuò)過(guò)了春招實(shí)習(xí)經(jīng)歷。 前言 只有光頭才能變強(qiáng) 離上次發(fā)文章已經(jīng)快兩個(gè)月時(shí)間了,最近一直忙著秋招的事。今天是2018年10月22日,對(duì)于互聯(lián)網(wǎng)行業(yè)來(lái)說(shuō),秋招就基本結(jié)束了。我這邊的流程也走完了(不再筆試/面試了),所以來(lái)寫(xiě)寫(xiě)我的秋招經(jīng)歷...

    qqlcbb 評(píng)論0 收藏1
  • 九款優(yōu)秀的企業(yè)項(xiàng)目協(xié)作工具推薦

    摘要:釘釘釘釘是阿里巴巴集團(tuán)專(zhuān)為中國(guó)企業(yè)打造的免費(fèi)溝通和協(xié)同的多端平臺(tái),提供版,版和手機(jī)版,支持手機(jī)和電腦間文件互傳。 1:@teamhttps://www.atteam.cn/項(xiàng)目協(xié)作管理,越復(fù)雜越有序,足夠簡(jiǎn)單足夠有效,@Team針對(duì)企業(yè)團(tuán)隊(duì)協(xié)作所遇到的困境而研發(fā)的新一代基于云服務(wù)的企業(yè)級(jí)協(xié)同工作平臺(tái),通過(guò)為每個(gè)企業(yè)或團(tuán)隊(duì)提供專(zhuān)屬的私密網(wǎng)絡(luò)空間和全新的協(xié)作方式,幫助企業(yè)實(shí)現(xiàn)高效便捷的跨部...

    lei___ 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<