フロントエンドの小さなプロジェクトのオンライン便利な貼り付け


次のような効果が得られます。


インタフェースがあまりきれいではないかもしれませんが、容器の高さが長くなることを考慮して、画像を背景にしていません.

プレビュー


便利シール

関連する知識点

  • sass(cssプリコンパイラ)
  • webpack(自動構築ツール、LESS、CSS、JSコンパイルと圧縮コードを実現)
  • express(Node.jsプラットフォームベースのweb開発フレームワーク)
  • html+css
  • Node.js(Chrome V 8エンジンベースのJavaScript実行環境)
  • jQuery(高速で簡潔なJavaScriptフレームワーク)
  • sequelize(ノードの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では、次の2つを追加します.
    このように書くと、端末で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.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, // , ? $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); } }); } } }

    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、ユーザー を したりして、これでほぼ しました.

    まとめ


    1 の を て、 、モジュール 、webpackおよびloaderとプラグインの 、npmの 、Expressの 、ルーティング、ミドルウェア、sqlite 3、nodejsを し、 で くの に した. えば、 の 、カンマをセミコロンに き え、 の がグローバル になったため、エラーが し、 い べた.しかし、この の でやはり くのことを んで、 なのは で、 き き の に かって きますか?