摘要:實現的效果如下界面可能不是太好看,考慮到容器的高度會被拉長,因此沒有用圖片做背景。
實現的效果如下:
界面可能不是太好看?,考慮到容器的高度會被拉長,因此沒有用圖片做背景。
預覽便利貼
涉及的知識點sass(css 預編譯器)
webpack(自動化構建工具,實現LESS,CSS,JS編譯和壓縮代碼)
express (基于 Node.js 平臺的 web 開發框架)
html+css
Node.js(基于 Chrome V8 引擎的 JavaScript 運行環境)
jQuery(一個快速、簡潔的JavaScript框架)
sequelize(Node的ORM框架Sequelize操作數據庫)
passport(實現第三方登錄)
實現功能github第三方登錄
添加筆記(登錄成功后)
刪除筆記
修改筆記
使用 markdown(類似 typroa)
筆記拖拽
準備工作必要條件:已經安裝好了node環境,還沒安裝的可以去node中文官網下載
小提示:如果用 npm 下載感覺慢的話,可以下載一個切換鏡像源的工具nrm,在終端輸入:
npm i nrm -g
然后如下操作:
開始!!
1.新建一個文件夾,名字自己起,打開終端,切換到自己新建文件夾,如:
cd (文件夾名稱)
2.生成 package.json
npm init -y
3.安裝 express
npm i express --save
4.安裝 express生成器:
npm install express-generator --save
5.生成 ejs 模板(類似 jsp 的寫法)
express -f -e npm i
其中public用來存放編譯后的js文件以及編譯好的css文件等,routes用來存放處理 ajax 的請求文件,views就是存放視圖文件
然后新建 database 和 src:
其中 src/js 里面 app 代表不同頁面的入口文件,lib 就是一些常用的庫,mod 就是你寫的一些模塊,database 用來存放數據庫數據的
6.輸入:
npm start
如果有出現下面的錯誤:
出現這個錯誤是因為你沒有下載模塊,只需在終端輸入:
npm i (模塊名) --save
就可以了
7.打開瀏覽器,輸入localhost:3000
出現下面這樣就說明成功了:
8.接下來安裝webpack和相關依賴
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 里建一個 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 抽離出來生成一個文件 }] }, 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 } }) ] }
說明
entry:入口文件,也就是 src/js/app里面的index.js,其中__dirname是獲得當前文件所在目錄的完整目錄名
output:輸出編譯后的文件 index.js,輸出到 public/js 里面
module:配置Loaders,通過使用不同的loader,webpack有能力調用外部的腳本或工具,實現對不同格式的文件的處理,比如說分析轉換scss為css,或者把下一代的JS文件
resolve.alias:設置模塊別名,便于我們更方便引用,比如說我在 js里面的文件需要 jquery,在里面的文件直接寫 require("jquery") 就行了
如果所有文件都需要 jquery,那么直接在 plugins里面寫成這樣:
就不需要 require 了
這個是壓縮文件的
10.在 package.json 中,增加如下兩條:
寫成這樣,你在終端就可以寫成npm run webpack 來編譯文件,
npm run watch來監控 src 里面的 js 和 scss 的變化,只要一修改,進行編譯,提高了效率
11.測試
你可以試試在 js 里面的 index.js寫點東西,然后 npm run webpack,如果終端顯示是這樣:
就證明成功了
項目思路邏輯比較簡單
首先用戶必須登錄才能添加筆記,當用戶失焦的時候,將數據插入數據庫,并且重新布局(瀑布流)
用戶不能更改其他用戶的筆記,除了管理員?
用戶更新筆記之后,數據庫的數據重新更新,重新布局
用戶可以刪除筆記,數據從數據庫中刪除,重新布局
用戶可以拖拽筆記,但不將位置存入數據庫
實現html,css就不講了,可以看看我的源碼,主要講 js。
1.瀑布流的實現
思路:(前提是必須絕對定位)
獲取元素的寬度
通過窗口的寬度除以元素的寬度來獲取列數
初始化一個數組來獲取每列的高度,初始化每列的高度為0
遍歷元素,獲取最小的的列數的高度和索引,對當前元素進行定位,列數高度加等于當前元素的高度
知道思路后,代碼很快就寫出來了:
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), //獲取列數 colSumHeight = []; //獲取每列的高度 //對每列的高度進行初始化 for (var i = 0; i < colNum; i++) { colSumHeight[i] = 0; } $items.each(function () { var $current = $(this); var index = 0, minSumHeight = colSumHeight[0]; //獲取最小的的列數的高度和索引 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); } //對當前元素進行定位 $current.animate({ left: nodeWidth * index, top: minSumHeight }, 5); colSumHeight[index] += $current.outerHeight(true); }); } //當窗口發生變化時,重新渲染 $(window).on("resize", function () { render($ct); }); return { init: render } })();
2.筆記的拖拽
我們先看個圖
因此代碼如下:
//設置筆記的移動 $noteHead.on("mousedown", function (e) { var evtX = e.pageX - $note.offset().left, //evtX 計算事件的觸發點在 dialog內部到 dialog 的左邊緣的距離 evtY = e.pageY - $note.offset().top; $note.addClass("draggable").data("evtPos", { x: evtX, y: evtY }); //把事件到 dialog 邊緣的距離保存下來 }).on("mouseup", function () { $note.removeClass("draggable").removeData("pos"); }); $("body").on("mousemove", function (e) { $(".draggable").length && $(".draggable").offset({ top: e.pageY - $(".draggable").data("evtPos").y, // 當用戶鼠標移動時,根據鼠標的位置和前面保存的距離,計算 dialog 的絕對位置 left: e.pageX - $(".draggable").data("evtPos").x }); }); },
3.提示模塊
這個比較容易:
/* 提示模塊 參數:狀態(1表示成功,0表示失敗),消息,出現時間(不寫默認是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 等等)
創建節點
設置顏色
綁定事件
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"), //默認存放 Note 的容器 context: "請輸入內容", //Note 的內容 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.$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, //記錄下坑,之前末尾是分號不是逗號后面都變成了全局變量結果造成了最后一個才能修改? $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() === "請輸入內容") $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) { //判斷是否有這個id,如果有就更新,如果沒有就添加 _this.edit($noteCt.html()) } else { _this.add($noteCt.html()) } } }); //設置筆記的移動 $noteHead.on("mousedown", function (e) { var evtX = e.pageX - $note.offset().left, //evtX 計算事件的觸發點在 dialog內部到 dialog 的左邊緣的距離 evtY = e.pageY - $note.offset().top; $note.addClass("draggable").data("evtPos", { x: evtX, y: evtY }); //把事件到 dialog 邊緣的距離保存下來 }).on("mouseup", function () { $note.removeClass("draggable").removeData("pos"); }); $("body").on("mousemove", function (e) { $(".draggable").length && $(".draggable").offset({ top: e.pageY - $(".draggable").data("evtPos").y, // 當用戶鼠標移動時,根據鼠標的位置和前面保存的距離,計算 dialog 的絕對位置 left: e.pageX - $(".draggable").data("evtPos").x }); }); }, /* 添加筆記到數據庫 */ 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); } }) }, /* 編輯筆記數據庫 */ 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("確認要刪除嗎?")) { $.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); } }); } } }" + this.username + "" + this.createTime + "" + "5.筆記管理模塊
var NoteManager = (function () { //頁面加載 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, "網絡異常"); }); } /* 添加筆記 */ 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.發布訂閱模式
/* 發布訂閱模式 */ 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 } })();寫完模塊后,寫入口文件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%了,接下來就創建數據庫,連接數據庫了
/*創建數據庫 運行 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") }); /* 測試連接是否成功 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(); */ /* //創建數據庫 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 請求,處理登錄信息,獲取 id,用戶名等等,到這就基本完成了
總結經過一星期的開發,了解了前后端聯調,模塊化開發方式、webpack 及loader和插件的使用、npm 的使用,Express的使用、路由、中間件、sqlite3、nodejs,在開發過程中還是有遇到許多問題,例如在連續聲明變量的時候,不小心把逗號寫成了分號,其他變量就變成了全局變量,于是就出錯了,查了好久?
不過在這過程之中還是學到了許多,重要的是過程,繼續往前端的路走下去?文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91788.html
摘要:然而這次的文章,就像賀師俊所說的這篇文章是從程序員這個老年度總結前端掘金年對我來說,是重要的一年。博客導讀總結個人感悟掘金此文著筆之時,已經在眼前了。今天,我就來整理一篇,我個人認為的年對開發有年終總結掘金又到 2016 Top 10 Android Library - 掘金 過去的 2016 年,開源社區異常活躍,很多個人與公司爭相開源自己的項目,讓人眼花繚亂,然而有些項目只是曇花一...
摘要:實現不定期更新技巧前端掘金技巧,偶爾更新。統一播放效果實現打字效果動畫前端掘金前端開源項目周報前端掘金由出品的前端開源項目周報第四期來啦。 Web 推送技術 - 掘金騰訊云技術社區-掘金主頁持續為大家呈現云計算技術文章,歡迎大家關注! 作者:villainthr 摘自 前端小吉米 伴隨著今年 Google I/O 大會的召開,一個很火的概念--Progressive Web Apps ...
摘要:具體的時間線從月中旬,我開始關注牛客網的秋招內推信息。直至十月中下旬結束秋招。之前也寫過自己在廣州找實習的經歷,那次把面試的過程都具體貼出來了。我今年就完美錯過了春招實習經歷。 前言 只有光頭才能變強 離上次發文章已經快兩個月時間了,最近一直忙著秋招的事。今天是2018年10月22日,對于互聯網行業來說,秋招就基本結束了。我這邊的流程也走完了(不再筆試/面試了),所以來寫寫我的秋招經歷...
摘要:釘釘釘釘是阿里巴巴集團專為中國企業打造的免費溝通和協同的多端平臺,提供版,版和手機版,支持手機和電腦間文件互傳。 1:@teamhttps://www.atteam.cn/項目協作管理,越復雜越有序,足夠簡單足夠有效,@Team針對企業團隊協作所遇到的困境而研發的新一代基于云服務的企業級協同工作平臺,通過為每個企業或團隊提供專屬的私密網絡空間和全新的協作方式,幫助企業實現高效便捷的跨部...
閱讀 1174·2021-09-27 13:34
閱讀 981·2021-09-13 10:25
閱讀 511·2019-08-30 15:52
閱讀 3450·2019-08-30 13:48
閱讀 648·2019-08-30 11:07
閱讀 2167·2019-08-29 16:23
閱讀 1993·2019-08-29 13:51
閱讀 2328·2019-08-26 17:42