SVG中的点击事件坐标

时间:2019-02-21 04:32:30

标签: javascript jquery svg click dom-events

此包含SVG的HTML:

queue()
    .defer(d3.json, "/data")
    .await(makeGraphs);

function makeGraphs(error, recordsJson) {

    //Clean data
    var records = recordsJson;
    var dateFormat = d3.time.format("%Y-%m-%d %H:%M:%S");

    records.forEach(function(d) {
        d["timestamp"] = dateFormat.parse(d["timestamp"]);
        d["timestamp"].setMinutes(0);
        d["timestamp"].setSeconds(0);
        d["longitude"] = +d["longitude"];
        d["latitude"] = +d["latitude"];
    });

    //Create a Crossfilter instance
    var ndx = crossfilter(records);

    //Define Dimensions
    var dateDim = ndx.dimension(function(d) { return d["timestamp"]; });
    var genderDim = ndx.dimension(function(d) { return d["gender"]; });
    var ageSegmentDim = ndx.dimension(function(d) { return d["age_segment"]; });
    var phoneBrandDim = ndx.dimension(function(d) { return d["phone_brand_en"]; });
    var locationdDim = ndx.dimension(function(d) { return d["location"]; });
    var allDim = ndx.dimension(function(d) {return d;});


    //Group Data
    var numRecordsByDate = dateDim.group();
    var genderGroup = genderDim.group();
    var ageSegmentGroup = ageSegmentDim.group();
    var phoneBrandGroup = phoneBrandDim.group();
    var locationGroup = locationdDim.group();
    var all = ndx.groupAll();


    //Define values (to be used in charts)
    var minDate = dateDim.bottom(1)[0]["timestamp"];
    var maxDate = dateDim.top(1)[0]["timestamp"];


    //Charts
    var numberRecordsND = dc.numberDisplay("#number-records-nd");
    var timeChart = dc.barChart("#time-chart");
    var genderChart = dc.rowChart("#gender-row-chart");
    var ageSegmentChart = dc.rowChart("#age-segment-row-chart");
    var phoneBrandChart = dc.rowChart("#phone-brand-row-chart");
    var locationChart = dc.rowChart("#location-row-chart");



    numberRecordsND
        .formatNumber(d3.format("d"))
        .valueAccessor(function(d){return d; })
        .group(all);


    timeChart
        .width(650)
        .height(140)
        .margins({top: 10, right: 50, bottom: 20, left: 20})
        .dimension(dateDim)
        .group(numRecordsByDate)
        .transitionDuration(500)
        .x(d3.time.scale().domain([minDate, maxDate]))
        .elasticY(true)
        .yAxis().ticks(4);

    genderChart
        .width(300)
        .height(100)
        .dimension(genderDim)
        .group(genderGroup)
        .ordering(function(d) { return -d.value })
        .colors(['#6baed6'])
        .elasticX(true)
        .xAxis().ticks(4);

    ageSegmentChart
        .width(300)
        .height(150)
        .dimension(ageSegmentDim)
        .group(ageSegmentGroup)
        .colors(['#6baed6'])
        .elasticX(true)
        .labelOffsetY(10)
        .xAxis().ticks(4);

    phoneBrandChart
        .width(300)
        .height(310)
        .dimension(phoneBrandDim)
        .group(phoneBrandGroup)
        .ordering(function(d) { return -d.value })
        .colors(['#6baed6'])
        .elasticX(true)
        .xAxis().ticks(4);

    locationChart
        .width(200)
        .height(510)
        .dimension(locationdDim)
        .group(locationGroup)
        .ordering(function(d) { return -d.value })
        .colors(['#6baed6'])
        .elasticX(true)
        .labelOffsetY(10)
        .xAxis().ticks(4);

    var map = L.map('map');

    var drawMap = function(){

        map.setView([31.75, 110], 4);
        mapLink = '<a href="http://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                attribution: '&copy; ' + mapLink + ' Contributors',
                maxZoom: 15,
            }).addTo(map);

        //HeatMap
        var geoData = [];
        _.each(allDim.top(Infinity), function (d) {
            geoData.push([d["latitude"], d["longitude"], 1]);
          });
        var heat = L.heatLayer(geoData,{
            radius: 10,
            blur: 20, 
            maxZoom: 1,
        }).addTo(map);

    };

    //Draw Map
    drawMap();

    //Update the heatmap if any dc chart get filtered
    dcCharts = [timeChart, genderChart, ageSegmentChart, phoneBrandChart, locationChart];

    _.each(dcCharts, function (dcChart) {
        dcChart.on("filtered", function (chart, filter) {
            map.eachLayer(function (layer) {
                map.removeLayer(layer)
            }); 
            drawMap();
        });
    });

    dc.renderAll();

};

和这个简单的jQuery click事件处理程序:

{% extends "base.html" %}
{% block content %}

<body class="application">


  <div class="container-fluid">

    <div class="row">

      <div class="col-sm-6">
        <div class="row">

          <!-- Time Chart -->
          <div class="col-sm-12">
            <div class="chart-wrapper">
              <div class="chart-title">
                Congestion Key Issues
              </div>
              <div class="chart-stage">
                <div id="time-chart"></div>
              </div>
            </div>
          </div>
          <!-- Time Chart -->

          <!-- Brand -->
          <div class="col-sm-6">
            <div class="chart-wrapper">
              <div class="chart-title">
                Heatmap
              </div>
              <div class="chart-stage">
                <div id="phone-brand-row-chart"></div>
              </div>
            </div>
          </div>
          <!-- Brand -->


          <div class="col-sm-6">

            <div class="row">
            <!-- Gender -->
            <div class="col-sm-12">
              <div class="chart-wrapper">
                <div class="chart-title">
                  Occupancy
                </div>
                <div class="chart-stage">
                  <div id="gender-row-chart"></div>
                </div>
              </div>
            </div>
            </div>
            <!-- Gender -->

            <!-- Age Segment -->
            <div class="row">
            <div class="col-sm-12">
              <div class="chart-wrapper">
                <div class="chart-title">
                  Speed
                </div>
                <div class="chart-stage">
                  <div id="age-segment-row-chart"></div>
                </div>
              </div>
            </div>
            <!-- Age Segment -->
          </div>
          </div>


        </div>
      </div>


      <div class="col-sm-2">
        <div class="row">

          <!-- Chinese Province -->
          <div class="col-sm-12">
            <div class="chart-wrapper">
              <div class="chart-title">
                Suburb
              </div>
              <div class="chart-stage">
                <div id="location-row-chart"></div>
              </div>
            </div>
          </div>
          <!-- Chinese Province -->
        </div>
      </div>



      <div class="col-sm-4">

        <div class="row">
          <!-- Map -->
          <div class="col-sm-12">
            <div class="chart-wrapper">
              <div class="chart-title">
                Map
              </div>
              <div class="chart-stage">
                <div id="map" style="width: 400px; height: 380px"></div>
              </div>
            </div>
          </div>
          <!-- Map -->
        </div>

        <div class="row">
          <!-- Number of events -->
          <div class="col-sm-12">
            <div class="chart-wrapper">
              <div class="chart-title">
                Speed Observation
              </div>
              <div class="chart-stage">
                <div id="number-records-nd"></div>
              </div>
            </div>
          </div>
          <!-- Number of events -->
        </div>

      </div>

    </div>

  </div>
</body>
    {% endblock %}

如下所示:https://jsfiddle.net/1ht0L8y6/6/在不同的浏览器中具有不同的行为:

Chrome浏览器:无论我在SVG中的哪个位置单击,坐标均基于SVG元素的左上角。这是我想要的行为。

Firefox :坐标基于我所处元素的左上角,这些元素可能是SVG,多边形或文本。

IE和Edge:

  • 在SVG中但不在其任何子元素中时,坐标 基于SVG元素。
  • 在多边形中时,坐标为 基于from flask_sqlalchemy import SQLAlchemy from flask import (Flask, request, session, g, redirect, url_for, abort, render_template, flash) import pandas as pd from shapely.geometry import Point, shape import json data_path = './input/' n_samples = 30000 def get_age_segment(age): if age <= 22: return '22-' elif age <= 26: return '23-26' elif age <= 28: return '27-28' elif age <= 32: return '29-32' elif age <= 38: return '33-38' else: return '39+' def get_location(longitude, latitude, provinces_json): point = Point(longitude, latitude) for record in provinces_json['features']: polygon = shape(record['geometry']) if polygon.contains(point): return record['properties']['name'] return 'other' with open(data_path + '/geojson/china_provinces_en.json') as data_file: provinces_json = json.load(data_file) app = Flask(__name__) # create the application instance :) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///streams.sqlite3' db = SQLAlchemy(app) @app.route('/') def index(): return render_template('index.html') @app.route('/Map') def Map(): return render_template('map.html') @app.route("/data") def get_data(): gen_age_tr = pd.read_csv(data_path + 'gender_age_train.csv') ev = pd.read_csv(data_path + 'events.csv') ph_br_dev_model = pd.read_csv(data_path + 'phone_brand_device_model.csv') df = gen_age_tr.merge(ev, how='left', on='device_id') df = df.merge(ph_br_dev_model, how='left', on='device_id') #Get n_samples records df = df[df['longitude'] != 0].sample(n=n_samples) top_10_brands_en = {'华为':'Huawei', '小米':'Xiaomi', '三星':'Samsung', 'vivo':'vivo', 'OPPO':'OPPO', '魅族':'Meizu', '酷派':'Coolpad', '乐视':'LeEco', '联想':'Lenovo', 'HTC':'HTC'} df['phone_brand_en'] = df['phone_brand'].apply(lambda phone_brand: top_10_brands_en[phone_brand] if (phone_brand in top_10_brands_en) else 'Other') df['age_segment'] = df['age'].apply(lambda age: get_age_segment(age)) df['location'] = df.apply(lambda row: get_location(row['longitude'], row['latitude'], provinces_json), axis=1) cols_to_keep = ['timestamp', 'longitude', 'latitude', 'phone_brand_en', 'gender', 'age_segment', 'location'] df_clean = df[cols_to_keep].dropna() return df_clean.to_json(orient='records') """ @app.route('/Analytics') def Analytics(): return render_template('analytics.html') """ 组的原点及其偏移量<div class="container"> <div class="spacer"></div> <svg> <g id="polygonGroup" transform="translate(80, 50)"> <polygon points="-60,-10 -35,-30 -10,-10 -10,30 -60,30"></polygon> <polygon points="10,-10 35,-30 60,-10 60,30 10,30"></polygon> <polygon class="origin" points="-4,0 0,4 4,0 0,-4"></polygon> </g> <g id="textGroup" transform="translate(80, 50)"> <text x="-35" y="10">Text</text> <text x="35" y="10">Text</text> </g> </svg> </div> (即黑钻石)。负坐标可以这样 与Chrome或Firefox不同。
  • 我观察到一种不同的行为 这些浏览器中的text元素:它们会根据 文本元素的底部中间。但是我无法 在小提琴中重现这一点;在小提琴中,文本元素的行为 与这些浏览器中的多边形相同。

什么是可靠的跨浏览器获取点击坐标的方法?

1 个答案:

答案 0 :(得分:4)

我在您的代码中添加了一个检测SVG中鼠标位置的功能。

let svg = document.querySelector('svg')

function clicked(event) {
  let m = oMousePosSVG(event);
    console.log(m.x,m.y);
}

svg.addEventListener("click", clicked)


function oMousePosSVG(e) {
      var p = svg.createSVGPoint();
      p.x = e.clientX;
      p.y = e.clientY;
      var ctm = svg.getScreenCTM().inverse();
      var p =  p.matrixTransform(ctm);
      return p;
}
svg{border:1px solid}
<div class="container">
  <div class="spacer"></div>
  <svg>
    <g id="polygonGroup" transform="translate(80, 50)">
      <polygon points="-60,-10 -35,-30 -10,-10 -10,30 -60,30"></polygon>
      <polygon points="10,-10 35,-30 60,-10 60,30 10,30"></polygon>
      <polygon class="origin" points="-4,0 0,4 4,0 0,-4"></polygon>
    </g>
    <g id="textGroup" transform="translate(80, 50)" fill="red">
      <text x="-35" y="10">Text</text>
      <text x="35" y="10">Text</text>
    </g>
  </svg>
</div>

要了解有关SVG中鼠标检测的更多信息,我推荐这本书:Using SVG with CSS3 and HTML5: Vector Graphics for Web Design

希望对您有帮助。