Flatten points into SVG polyline Polymer

2019-05-30 05:15发布

I have the following data structure:

'points': [
  {
    'x': 5535,
    'y': 5535
  },
  {
    'x': 5535,
    'y': 60000
  },
  {
    'x': 60000,
    'y': 60000
  },
  {
    'x': 60000,
    'y': 5535
  }
];

I would like to flatten it to 5535,5535 5535,60000 60000,60000 60000,5535 to use as a polyline points attribute.

I have the following in Polymer <template>

<polyline
    id="polygon",
    points="{{points | polygonPoints | toSvgPoints(width, height)}}"
    stroke="rgba({{colour.r}}, {{colour.g}}, {{colour.b}}, {{opacity}})"
    fill="rgba({{colour.r}}, {{colour.g}}, {{colour.b}}, {{opacity * 0.6}})"
    stroke-width="{{lineWidth}}"
    stroke-linecap="round"
    stroke-linejoin="round"
/>

Where polygonPoints and toSvgPoints filters look like so:

/**
 * Retrieves the polygon points for this object.
 * @param point optionally provide a list of normalized points
 * @returns the polygon points or all points if a line
 */
polygonPoints: function(points) {
  var array = points.slice(0);
  points = points || this.points;
  array.push(points[0])
  return array;
},

/**
 * Retrieves the polygon points for this object.
 * @param point optionally provide a list of normalized points
 * @returns the polygon points or all points if a line
 */
toSvgPoints: function(points, width, height) {
  var i, string = '';
  points = points || this.points;
  for (i = 0; i < points.length; ++i) {
    string += this.normalizedToActual(points[i].x, width);
    string += ',';
    string += this.normalizedToActual(points[i].y, height);
    string += ' ';
  }
  return string;
},

This works, the toSvgPoints returns the correct points but the bindings to the point values do not get set up automagically by Polymer. For example, if I modify this.points[0].x = 4219 the polygon doesn't update because the binding hasn't been created to the polygon attribute.

Is this something that just can't be solved without providing some other method that invokes a redraw? Ideally I'd just want to do this:

<polyline
    id="polygon",
    points="{{x,y for (points | polygonPoints)}}"
    ...
/>

Which would stamp out the x and y values in the points attribute and set up the bindings.

标签: svg polymer
3条回答
Luminary・发光体
2楼-- · 2019-05-30 05:59

PolymerExpressions only observes objects that are directly referenced in an expression, so in this case it observes points, but not the properties on the elements in the array. If you replace a point, the expression will update, but it won't if you modify a point.

There are a few ways to deal with this. If you know where a point is being modified, at you have a reference to the list there, you can manually notify on the list. I usually work with Polymer.Dart, but I think that the notify() function in observe-js is what you're looking for: https://github.com/Polymer/observe-js/blob/master/src/observe.js#L1076

Alternatively, instead of returning a static projection of the points, have toSvgPoints listen to all of its dependencies: points, x and y for each point, width and height. When an input changes, update the output array. This will cause a chain of updates that propagates to your view.

Use Polymer's observe-js library to do observation. It polyfills Object.observe() on platforms that don't have it. https://github.com/Polymer/observe-js#objectobserver

查看更多
爷、活的狠高调
3楼-- · 2019-05-30 06:12

I don't know for sure but I guess Polymer doesn't observe individual attributes inside an array. You could try to replace the item at that position:

this.points[0] = {x:4219,y:this.points[0].y}

or alternatively create a new array and set it to this.points ?

查看更多
神经病院院长
4楼-- · 2019-05-30 06:12

Well that took ages. Don't use a polyline use a path:

<path
    id="zone"
    on-down="{{dragStart}}"
    on-up="{{dragEnd}}"
    d="M {{points[0].x| max(width)}} {{points[0].y | max(height)}} {{points | polygonPoints | toSvgPoints(width, height)}}"
    fill="rgba({{colour.r}}, {{colour.g}}, {{colour.b}}, {{(selected ? 0.8 : 0.6) * 0.6}})"
    stroke="rgba({{colour.r}}, {{colour.g}}, {{colour.b}}, {{(selected ? 0.8 : 0.6)}})"
    stroke-linecap="round"
    stroke-linejoin="round"
/>

The M {{points[0].x| max(width)}} {{points[0].y | max(height)}} at the start of the d attribute forces the redraw.

查看更多
登录 后发表回答