svgベースの落書きコンポーネントを設計する(一)


svgに基づいて落書きコンポーネントを書き、プロジェクトの前にいくつかの効果図を添付すると言いました.
プロジェクトアドレス:SVGraffiti
紙面の問題で,本稿ではまずプロジェクトの概要を全体的に紹介し,コンポーネント間の通信方式に重点を置いて紹介する.
一、項目説明
このプロジェクトは[email protected]構築されたマルチページアプリケーションは、ES 6を使用して開発され、コンポーネントでコードを整理します.git cloneプロジェクト後(文末にこのプロジェクトgithub倉庫アドレスを添付)、npm iインストール依存、npm run dev実行プロジェクト、デフォルトではアプリケーションのトップページ、つまり上の効果プレビューに対応するインタフェースが開きます.開発プロセスでは、いくつかの機能のテストコードが個別に作成されるため、このプロジェクトでは、次のような異なるページに対応する機能が提供されます.
color pickerコンポーネントテストページ:
コンポーネントメッセージ通信フレームワークのテストページ:
svg最下位ペイントapiテストページ:
二、コンポーネント間通信
1.コンポーネント間では、最大限のパッケージングとデカップリングを実現するために、直接通信するのではなく、「メッセージサブスクリプション/パブリッシュ管理センター」(以下、「メッセージセンター」と略称する)を介して間接通信を行う.コンポーネントは、自分が異なる役割であることを宣言することによって、対応する通信能力を有します.
  • コンポーネントはサブスクライバとして宣言され、@Topics注釈の形式で「メッセージセンター」から自分の興味のあるトピックメッセージを購読し、対応するメッセージはnotifyインタフェースを通じてコンポーネントに通知されます.
  • コンポーネントはパブリッシャ(Publisher)として宣言され、Publisherロールが注入したpublishメソッドによってトピックメッセージをパブリッシャすることができる.
  • コンポーネントは、サブスクライバとパブリッシャとの通信能力を同時に有するパブリッシャ/サブスクライバ(SubScatterer)として宣言される.

  • ここでは、プロジェクトの中間領域のパネルコンポーネントを例にとると、パネルコンポーネントは、Toolbarコンポーネントからの切り替えペイント能力、空のペイント内容、およびSettingsコンポーネントからの設定ペイントパラメータ情報を受信するだけであるため、このコンポーネントはメッセージ購読者ロールにすぎず、以下のように符号化設計されている.
    まず、対応するロールクラスをインポートします.
    import Subscriber from '../../supports/pubsub/base/subscriber';
    import Topics from '../../supports/pubsub/base/topics';

    対応するコンポーネントの作成:
    //   @Topics             
    @Topics(['function', 'resident_function', 'set_preference'])
    export default class Sketchpad extends Subscriber {
        //    
        constructor(sketchpad) {
            super();
            this.sketchpad = sketchpad;
            // ...
        }
        
        /**
         *     【PubSub      】    ,                  
         * 1、  Toolbar      “        ” ,        :“function”
         * 2、  Toolbar      “      ” ,        :“resident_function”
         * 3、  Settings      “        ” ,        :“set_preference”
         * @param {String} topic       
         * @param {Object} entity       
         */
        notify(topic, entity) {
           //           
        }
    }

    注意:@Topicsは静的であり、一部のトピックが実行時にサブスクリプションする必要がある場合は、Subscriberロールが提供するsubscribeメソッドを呼び出してメッセージを動的にサブスクリプションすることもできます.
    2、PubSub(メッセージ購読/配布管理センター)の実現は、下位レベルの汎用能力である以上、具体的な業務を持たずに実現しなければならない.命名規範においても符号化実現においても、汎用モジュールであることを保証しなければならない.
    PubSubの実装:
    /**
     *         
     */
    export default class PubSub {
    
        //              
        static topics = {};
    
        /**
         *       
         * @param {String} topic   
         * @param {*} entity     
         */
        static publish(topic, entity) {
            if (!PubSub.topics[topic]) return;
    
            //            
            const subscribers = PubSub.topics[topic];
    
            //                 
            for (let subscriber of subscribers) {
                subscriber.notify && subscriber.notify(topic, entity);
            }
        }
    
        /**
         *         
         * @param {String} topic 
         */
        static registerTopic(topic) {
            const topics = PubSub['topics'];
            !topics[topic] && (topics[topic] = []);
        }
    
        /**
         *         
         * @param {Array} topics 
         */
        static registerTopics(topics = []) {
            topics.forEach(topic => {
                this.registerTopic(topic);
            });
        }
    
        /**
         *        
         * @param {String} topic   
         * @param {Object} subscriber    notify      
         */
        static addSubscriber(topic, subscriber) {
            const topics = PubSub['topics'];
            !topics[topic] && (topics[topic] = []);
    
            //                 
            topics[topic].push(subscriber);
        }
        
        /**
         *         
         * @param subscriber 
         */
        static removeSubscriber(subscriber) {
            const subs = [];
            //              ,        
            const topics = PubSub.topics;
            Object.keys(topics).forEach(topicName => {
                const topic = topics[topicName];
                for (let i = 0; i < topic.length; ++i) {
                    if (topic[i] === subscriber) {
                        subs.push(topics[topic].splice(i, 1));
                        break;
                    }
                }
            });
            return subs;
        }
    }

    Subscriberの実装:
    import PubSub from '../pubsub';
    
    const addSubscribe = (topics = [], context) => {
        topics.forEach(topic => {
            PubSub.addSubscriber(topic, context);
        });
    }
    
    /**
     *      
     */
    export default class Subscriber {
        constructor() {
            addSubscribe(this.__proto__.constructor.topics, this);
        }
    
        subscribe(topic) {
            PubSub.addSubscriber(topic, this);
        }
    }

    トピックの購読を容易にするために、@Topics注釈をもう1つ提供します.
    import PubSub from '../pubsub';
    
    /**
     *         
     * @param {Array} topics
     */
    export default function Topics(topics) {
        return target => {
            target.topics = topics;
            PubSub.registerTopics(topics);
        }
    }

    Publisherの実装:
    import PubSub from '../pubsub';
    
    /**
     *        
     */
    export default class Publisher {
        publish(topic, entity) {
            PubSub.publish(topic, entity);
        }
    }

    SubScattererの実装:
    import PubSub from '../pubsub';
    import Subscriber from './subscriber';
    
    /**
     *       and        
     */
    export default class SubScatterer extends Subscriber {
        publish(topic, entity) {
            PubSub.publish(topic, entity);
        }
    }

    本編では、プロジェクトの概要を説明し、コンポーネント間の通信をパブリッシュ/サブスクリプションで実現する方法を重点的に分析した.次に、「svg下位ペイント能力のパッケージ」、「画板の異なるペイント状態の実現と管理」、「共通のColorPickerをどのように開発するか」など、本プロジェクトに関連する文章をいくつか時間を割いて書く.親に頼むのが下手だ.
    プロジェクトgithubアドレス:SVGraffiti
    興味のある学生たちはstarと一緒に交流することを歓迎します.