HTML5 Canvas はインタラクティブな地下鉄路線図を実装します

不言
リリース: 2018-07-27 10:03:13
オリジナル
3916 人が閲覧しました

この記事では、HTML5 Canvas に基づいたインタラクティブな地下鉄路線図を紹介します。これは参考になるもので、困っている友人に役立つことを願っています。

はじめに

2日前のechartsについて インターネットでインスピレーションを探していたとき、同様の地図や地図の位置などの例をたくさん見ましたが、地下鉄の路線図はないようだったので、このインタラクティブな地下鉄の路線図をいじってみました。 デモでは、地下鉄のポイントがインターネットからランダムにダウンロードされました。この記事は、私が得た成果の一部とコードの実装を記録したもので、一部の友人に役立つことを願っています。もちろん、ご意見がございましたら、直接言っていただければ幸いです。

レンダリング

HTML5 Canvas はインタラクティブな地下鉄路線図を実装します

http://www.hightopo.com/demo/subway/index.html

地図の内容が少し多すぎます。すべてを表示したい場合は、フォントが少し表示されます。小さいですが、必要に応じて変更できます。結局のところ、フォントと描画されたコンテンツはすべてベクターで描画されます~

インターフェースの生成

。 ht.graph.GraphView コンポーネントを通じて生成されます。その後、HT for Web を使用して適切なメソッドを提供できます。キャンバス ブラシを呼び出して気軽に描画できます。まず、基礎となる div を生成する方法を見てみましょう:

var dm = new ht.DataModel();//数据容器
var gv = new ht.graph.GraphView(dm);//拓扑组件
gv.addToDOM();//将拓扑图组件添加进body中
ログイン後にコピー

addToDOM 関数は次のとおりです。次のように宣言されています:

addToDOM = function(){   
    var self = this,
        view = self.getView(),   
        style = view.style;
    document.body.appendChild(view); //将组件底层div添加到body中           
    style.left = '0';//默认组件是绝对定位,所以要设置位置
    style.right = '0';
    style.top = '0';
    style.bottom = '0';      
    window.addEventListener('resize', function () { self.iv(); }, false); //窗口变化事件           
}
ログイン後にコピー

これで、この div に落書きすることができます~ まず、それをダウンロードし、subway.js の地下鉄路線図にポイントを配置します。この js ファイルは、ダウンロードしたすべてのコンテンツであり、他には何も変更しません。主なことは、これらの点を線に従って配列に追加することです。例:

mark_Point13 = [];//线路 数组内包含线路的起点和终点坐标以及这条线路的名称
t_Point13 = [];//换成站点 数组内包含线路中的换乘站点坐标以及换成站点名称
n_Point13 = [];//小站点 数组内包含线路中的小站点坐标以及小站点名称
mark_Point13.push({ name: '十三号线', value: [113.4973,23.1095]}); 
mark_Point13.push({ name: '十三号线', value: [113.4155,23.1080]}); 
t_Point13.push({ name: '鱼珠', value: [113.41548,23.10547]}); 
n_Point13.push({ name: '裕丰围', value: [113.41548,23.10004]});
ログイン後にコピー

次に、地下鉄の路線を描画するために、js ですべての地下鉄の路線の番号と色を保持する配列 lineNum を宣言しました。すべての地下鉄路線の色とこれらの色のインデックスを保持する配列 lineNum の地下鉄路線番号のインデックスと 1 対 1 の対応があります:

var lineNum = ['1', '2', '3', '30', '4', '5', '6', '7', '8', '9', '13', '14', '32', '18', '21', '22', '60', '68'];
var color = ['#f1cd44', '#0060a1', '#ed9b4f', '#ed9b4f', '#007e3a', '#cb0447', '#7a1a57', '#18472c', '#008193', '#83c39e', '#8a8c29', '#82352b', '#82352b', '#09a1e0', '#8a8c29', '#82352b', '#b6d300', '#09a1e0'];
ログイン後にコピー

次に lineNum をトラバースし、要素と色を渡しますcreateLine 関数に lineNum を指定し、これら 2 つのパラメータに基づいて地下鉄の路線とカラー マッチングを描画します。 js ファイルの命名方法も規則的で、名前の後に対応する番号が追加されるので、文字列とこの番号を組み合わせるだけで js を取得できます。 対応する配列は次のとおりです。

let lineName = 'Line' + num;
let line = window[lineName];
ログイン後にコピー

createLine の定義も非常に単純です。私のコードは多くのスタイルを設定しているので、少し複雑に見えます。 ht.Polyline パイプラインを作成します。渡すことができます。 Polyline.addPoint() 関数はこの変数に特定のポイントを追加し、setSegments を通じてポイントの接続方法を設定できます。

function createLine(num, color) {//绘制地图线
    var polyline = new ht.Polyline();//多边形 管线
    polyline.setTag(num);//设置节点tag标签,作为唯一标示
    
    if(num === '68') polyline.setToolTip('A P M');//设置提示信息 
    else if(num === '60') polyline.setToolTip('G F'); 
    else polyline.setToolTip('Line' + num);
    if(color) {
        polyline.s({//s 为 setStyle 的简写,设置样式
            'shape.border.width': 0.4,//设置多边形的边框宽度
            'shape.border.color': color,//设置多边形的边框颜色
            'select.width': 0.2,//设置选中节点的边框宽度
            'select.color': color//设置选中节点的边框颜色
        });
    }
    let lineName = 'Line' + num;
    let line = window[lineName];
    for(let i = 0; i < line.length; i++) {
        for(let j = 0; j < line[i].coords.length; j++) {
            polyline.addPoint({x: line[i].coords[j][0]*300, y: -line[i].coords[j][1]*300});
            if(num === &#39;68&#39;){//APM线(有两条,但是点是在同一个数组中的)
                if(i === 0 && j === 0) {
                    polyline.setSegments([1]);
                }
                else if(i === 1 && j === 0) {
                    polyline.getSegments().push(1);
                }
                else {
                    polyline.getSegments().push(2);
                }
            }    
        }
    }
    polyline.setLayer(&#39;0&#39;);//将线设置在下层,点设置在上层“top”
    dm.add(polyline);//将管线添加进数据容器中储存,不然这个管线属于“游离”状态,是不会显示在拓扑图上的
    return polyline;
}
ログイン後にコピー

上記のコードで地下鉄の線にポイントを追加する場合は、いくつかの状況があります。これは、Line68 では、js で線を設定するときに「ジャンプ」ポイント現象が発生するため、スペースが限られています。 Line68 配列の詳細 宣言については、subway.js を参照してください。

ここで注意すべき点は、addPoint 関数を使用してセグメントを設定しない場合、追加されたポイントはデフォルトで直線で結ばれることです。セグメントの定義は次のとおりです:

1: moveTo、1 つのポイントを占有します。新しいパスを表す情報 開始点

2: lineTo、1 点を占める 最後の点からこの点までの接続を表す情報

3:quadraticCurveTo、2 点を占める 情報、最初の点は曲線制御点として使用される、2 番目の点は曲線の終点として使用されます

4: bezierCurveTo は 3 つの点情報を占め、1 番目と 2 番目の点は曲線の制御点として使用され、3 番目の点は曲線の終点として使用されます

5: closePath は、ポイント情報を占有せず、このパス描画の終了点とパスの開始点に近いことを表します

そこで、「ジャンプ」動作を実行し、セグメントを 1 に設定したいと考えています。

最後に地下鉄の線路上にこれらの点を描画します。この部分も、「mark_Point」、「t_Point」、「n_Point」で始まります。中指を動かして上にスワイプして見てみましょう。

これらのポイントの位置に ht.Node ノードを追加します。dm データコンテナにノードを追加すると、トポロジマップで設定されたデータコンテナが当然前提となります。コンポーネント gv はこの dm です。スペースが限られているため、地下鉄路線にポイントを追加するためのコード部分のみを示します:

var tName = &#39;t_Point&#39; + num;
var tP = window[tName];//大站点
if(tP) {//有些线路没有“换乘站点”
    for(let i = 0; i < tP.length; i++) {
        let node = createNode(tP[i].name, tP[i].value, color[index]);//在获取的线路上的点的坐标位置添加节点
        node.s({//设置节点的样式style
            &#39;label.scale&#39;: 0.05,//文本缩放,可以避免浏览器限制的最小字号问题
            &#39;label.font&#39;: &#39;bold 12px arial, sans-serif&#39;//设置文本的font
        });
        node.setSize(0.6, 0.6);//设置节点大小。由于js中每个点之间的偏移量太小,所以我不得不把节点设置小一些
        node.setImage(&#39;images/旋转箭头.json&#39;);//设置节点的图片
        node.a(&#39;alarmColor1&#39;, &#39;rgb(150, 150, 150)&#39;);//attr属性,可以在这里面设置任何的东西,alarmColor1是在上面设置的image的json中绑定的属性,具体参看 HT for Web 矢量手册(http://www.hightopo.com/guide/guide/core/vector/ht-vector-guide.html#ref_binding)
        node.a(&#39;alarmColor2&#39;, &#39;rgb(150, 150, 150)&#39;);//同上
        node.a(&#39;tpNode&#39;, true);//这个属性设置只是为了用来区分“换乘站点”和“小站点”的,后面会用上
    }
}
ログイン後にコピー

すべての地下鉄路線と駅が追加されました。しかし!描画したグラフが小さすぎて見えない可能性があります。このとき、graphView トポロジ コンポーネントの FitContent 関数を設定して、トポロジ グラフ上のすべてを移動不能に設定することもできます。

gv.fitContent(false, 0.00001);//自适应大小,参数1为是否动画,参数2为gv与边框的padding值
gv.setMovableFunc(function(){
    return false;//设置gv上的节点不可移动
});
ログイン後にコピー

これで地下鉄の路線図が表示できるようになりました~ やりとりを見てみましょう。

インタラクション

首先是鼠标移动事件,鼠标滑过具体线路时,线路会变粗,悬停一会儿还能看到这条线路的编号;当鼠标移动到“换乘站点”或“小站点”,站点对应的图标都会变大并且变色,字体也会变大,鼠标移开图标变回原来的颜色并且字体变小。不同点在于鼠标移动到“换乘站点”时,“换乘站点”会旋转。

鼠标滑动事件,我直接基于 gv 的底层 div 进行的 mousemove 事件,通过 ht 封装的 getDataAt 函数传入事件 event 参数,获取事件下对应的节点,然后就可以随意操作节点了:

gv.getView().addEventListener(&#39;mousemove&#39;, function(e) {
    var data = gv.getDataAt(e);//传入逻辑坐标点或者交互event事件参数,返回当前点下的图元
    if(name) {
        originNode(name);//不管什么时候都要让节点保持原来的大小
    }
    if (data instanceof ht.Polyline) {//判断事件节点的类型
        dm.sm().ss(data);//选中“管道”
        name = &#39;&#39;;
        clearInterval(interval);
    }
    else if (data instanceof ht.Node) {
        if(data.getTag() !== name && data.a(&#39;tpNode&#39;)) {//若不是同一个节点,并且mousemove的事件对象为ht.Node类型,那么设置节点的旋转
            interval = setInterval(function() {
                data.setRotation(data.getRotation() - Math.PI/16); //在自身旋转的基础上再旋转
            }, 100);
        }
        if(data.a(&#39;npNode&#39;)) {//如果鼠标移到“小站点”也要停止动画
            clearInterval(interval);
        }
        expandNode(data, name);////自定义的放大节点函数,比较容易,我不粘代码了,可以去http://hightopo.com/   查看
        dm.sm().ss(data);//设置选中节点
        name = data.getTag();//作为“上一个节点”的存储变量,可以通过这个值来获取节点
    }
    else {//其他任何情况则不选中任何内容并且清除“换乘站点”上的动画
        dm.sm().ss(null);
        name = &#39;&#39;;
        clearInterval(interval);
    }
});
ログイン後にコピー

鼠标悬停在地铁线路上时显示“具体线路信息”,我是通过设置 tooltip 来完成的(注意:要打开 gv 的 tooltip 开关):

gv.enableToolTip();//打开 tooltip 的开关
if(num === &#39;68&#39;) polyline.setToolTip(&#39;A P M&#39;);//设置提示信息 
else if(num === &#39;60&#39;) polyline.setToolTip(&#39;G F&#39;); 
else polyline.setToolTip(&#39;Line&#39; + num);
ログイン後にコピー

然后我利用右下角的 form 表单,单击表单上的具体线路,或者双击拓扑图上任意一个“站点”或者线路,则拓扑图会自适应到对应的部分,将被双击的部分展现到拓扑图的中央。

form 表单的声明部分我好像还没有解释。。。就是通过 new 一个 ht.widget.FomePane 类创建一个 form 表单组件,通过 form.getView() 获取表单组件的底层 div,将这个 div 摆放在 body 右下角,然后通过 addRow 函数向 form 表单中添加一行的表单项,可以在这行中添加任意多个项,通过 addRow 函数的第二个参数(一个数组),对添加进的表单项进行宽度的设置,通过第三个参数设置这行的高度:

function createForm() {//创建右下角的form表单
    var form = new ht.widget.FormPane();
    form.setWidth(200);//设置表单宽度
    form.setHeight(416);//设置表单高度
    let view = form.getView();
    document.body.appendChild(view);//将表单添加进body中
    view.style.zIndex = 1000;
    view.style.bottom = &#39;10px&#39;;//ht组件几乎都设置绝对路径
    view.style.right = &#39;10px&#39;;
    view.style.background = &#39;rgba(211, 211, 211, 0.8)&#39;;
    names.forEach(function(nameString) {
        form.addRow([//向表单中添加行
            {//这一行中的第一个表单项
                button: {//向表单中添加button按钮
                    icon: &#39;images/Line&#39;+nameString.value+&#39;.json&#39;,//设置按钮的图标
                    background: &#39;&#39;,//设置按钮的背景
                    borderColor: &#39;&#39;,//设置按钮的边框颜色
                    clickable: false//设置按钮不可点击
                }
            },
            {//第二个表单项
                button: {
                    label: nameString.name,
                    labelFont: &#39;bold 14px arial, sans-serif&#39;,
                    labelColor: &#39;#fff&#39;,
                    background: &#39;&#39;,
                    borderColor: &#39;&#39;,
                    onClicked: function() {//按钮点击回调事件
                        gv.sm().ss(dm.getDataByTag(nameString.value));//设置选中按下的按钮对应的线路
                        gv.fitData(gv.sm().ld(), true, 5);//将选中的地铁线路显示在拓扑图的中央
                    }
                }
            }
        ], [0.1, 0.2], 23);//第二个参数是设置第一参数中的数组的宽度,小于1是比例,大于1是实际宽度。第三个参数是该行的高度
    });
}
ログイン後にコピー

单击“站点”显示红色标注,双击节点自适应放置到拓扑图中央以及双击空白处将红色标注隐藏的内容都是通过对拓扑组件 gv 的事件监听来控制的,非常清晰易懂,代码如下:

var node = createRedLight();//创建一个新的节点,显示为“红灯”的样式
gv.mi(function(e) {//ht 中拓扑组件中的事件监听
    if(e.kind === &#39;clickData&#39; && (e.data.a(&#39;tpNode&#39;) || e.data.a(&#39;npNode&#39;))) {//e.kind获取当前事件类型,e.data获取当前事件下的节点
        node.s(&#39;2d.visible&#39;, true);//设置node节点可见
        node.setPosition(e.data.getPosition().x, e.data.getPosition().y);//设置node的坐标为当前事件下节点的位置
    }
    else if(e.kind === &#39;doubleClickData&#39;) {//双击节点
        gv.fitData(e.data, false, 10);//将事件下的节点自适应到拓扑图的中央,参数1为自适应的节点,参数2为是否动画,参数3为gv与边框的padding
    }
    else if(e.kind === &#39;doubleClickBackground&#39;) {//双击空白处
        node.s(&#39;2d.visible&#39;, false);//设置node节点不可见 查看 HT for Web 样式手册(http://www.hightopo.com/guide/guide/core/theme/ht-theme-guide.html#ref_style)
    }
});
ログイン後にコピー

注意 s(style) 和 a(attr) 定义是这样的,s 是 ht 预定义的一些样式属性,而 a 是我们用户来自定义的属性,一般是通过调用字符串来调用结果的,这个字符串对应的可以是常量也可以是函数,还是很灵活的。

最后还做了一个小小的部分,选中“站点”,则该“站点”的上方会显示一个红色的会“呼吸”的用来注明当前选中的“站点”。

HTML5 Canvas はインタラクティブな地下鉄路線図を実装します

“呼吸”的部分是利用 ht 的 setAnimation 函数来完成的,在用这个函数之前要先打开数据容器的动画开关,然后设置动画:

dm.enableAnimation();//打开数据容器的动画开关
function createRedLight() {
    var node = new ht.Node();
    node.setImage(&#39;images/红灯.json&#39;);//设置节点的图片
    node.setSize(1, 1);//设置节点的大小
    node.setLayer(&#39;firstTop&#39;);//设置节点显示在gv的最上层
    node.s(&#39;2d.visible&#39;, false);//节点不可见
    node.s(&#39;select.width&#39;, 0);//节点选中时的边框为0,不可见
    node.s(&#39;2d.selectable&#39;, false);//设置这个属性,则节点不可选中
    node.setAnimation({//设置动画 具体参见 HT for Web 动画手册(http://www.hightopo.com/guide/guide/plugin/animation/ht-animation-guide.html)
        expandWidth: {
            property: "width",//设置这个属性,并且未设置 accessType,则默认通过 setWidth/getWidth 来设置和获取属性。这里的 width 和下面的 height 都是通过前面设置的 size 得到的
            from: 0.5, //动画开始时的属性值
            to: 1,//动画结束时的属性值
            next: "collapseWidth"//字符串类型,指定当前动画完成之后,要执行的下个动画,可将多个动画融合
        },
        collapseWidth: {
            property: "width",
            from: 1, 
            to: 0.5,
            next: "expandWidth"
        },
        expandHeight: {
            property: "height",
            from: 0.5, 
            to: 1,
            next: "collapseHeight"
        },
        collapseHeight: {
            property: "height",
            from: 1, 
            to: 0.5,
            next: "expandHeight"
        },
        start: ["expandWidth", "expandHeight"]//数组,用于指定要启动的一个或多个动画
    });
    dm.add(node);
    return node;
}
ログイン後にコピー

全部代码结束!

总结

这个 Demo 花了我两天时间完成,总觉得有点不甘心啊,但是有时候思维又转不过弯来,花费了不少的时间,但是总的来说收获还是很多的,我以前一直以为只要通过 getPoints().push 来向多边形中添加点就可以了,求助了大神之后,发现原来这个方法不仅绕弯路而且还会出现各种各样的问题,比如 getPoints 之前,一定要在多边形中已经有 points 才可以,但是在很多情况下,初始化的 points 并不好设置,而且会造成代码很繁琐,直接通过 addPoint 方法,直接将点添加进多边形变量中,并且还会默认将点通过直线的方式连接,也不用设置 segments,多可爱的一个函数。

还有就是因为 ht 默认缩放大小是 20,而我这个 Demo 的间距又很小,导致缩放到最大地铁线路图显示也很小,所以我在 htconfig 中更改了 ht 的默认 zoomMax 属性,记住,更改这个值一定要在所有的 ht 调用之前,因为在 htconfig 中设置的值在后面定义都是不可更改的。

相关推荐:

使用h5 canvas实现时钟的动态效果

HTML5 Canvas API制作简单的猜字游戏

以上がHTML5 Canvas はインタラクティブな地下鉄路線図を実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!