Highcharts: circle with image background - highcharts

i'm using Highcharts to create a pie, and I need to put user's profile image in a circle in the middle of it. I have managed to add the image but couldn't get it to be round, and I managed to add a circle but without image background :/
what it the best way to combine the tow?
by the way it must be part of the svg and not an absolute div on top of the pie, because the tooltip needs to be on top of that image with opacity
to add a circle I used this code :
var pixelX = 438;
var pixelY = 276;
var pixelR = 70;
// add my circle
chart.renderer.circle(pixelX, pixelY, pixelR)
.attr({
zIndex: 100,
align: 'center',
// fill: 'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)',
color: 'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)',
stroke: 'black',
'stroke-width': 2
})
.css({
backgroundImage :'url(https://lh6.googleusercontent.com/-gaAgFzRLxQQ/AAAAAAAAAAI/AAAAAAAAAjc/ies0iU4BEqU/photo.jpg)'
})
.add();
});
this example shows how to do it with pure SVG : http://jsfiddle.net/9zkfodwp/1/

resolved it by defining a pattern with the img, works fine but now need to find a way for the pattern to be no-repeat.
code used to add pattern:
function(chart) { // on complete
var r = chart.renderer,
pattern = r.createElement('pattern')
.attr({
id: 'pattern',
patternUnits: 'userSpaceOnUse',
x: 0,
y: 0,
width: 180,
height: 190,
viewBox: '0 0 135 135'
})
.add(r.defs);
r.rect(0, 0, 135, 135, 0)
.attr('fill', '#ddd')
.add(pattern);
r.image(profileImg,0,0,135,135)
.add(pattern);
});
and when I add the circle it can have a fill of the pattern:
// add my circle
this.circle = chart.renderer.circle(pixelX, pixelY, pixelR).attr({
fill: 'url(#pattern)'
});
this.circle.add();

Related

Vue-Konva: Is there a way to reorder layers on the fly?

So, I've been working with vue-konva and I have something like this:
<v-container>
<v-stage ref="stage">
<v-layer ref="baseImage">
<v-image>
</v-layer>
<v-layer ref="annotationLayer">
<v-rect ref="eventBox">
<v-rect ref="rubberBox">
<v-rect ref="annotationRect">
</v-layer>
</v-stage>
<v-container>
Currently there are some issues if I want to draw new boxes, when there are other annotationRects already on the image. Because they are technically above the eventBox and rubberbox, they are "blocking" these two layers when the cursor is above an existing annotationRect.
But, I don't want to just constantly have eventBox and rubberBox be on top of annotationRect because I need to be able to interact with annotationRect to move them, resize them ,etc.
Is there a way for me to reorder eventBox, rubberBox, and annotationRect, i.e. changing the order to (bottom to top) annotationRect-eventBox-rubberBox from the original eventBox-rubberBox-annotationRect and back, on the fly, for example when the vue component receives an event from another component?
You need to define your eventBox, rubberBox, and annotationRect inside order array in the state of your app. Then you can use v-for directive to render items from the array:
<template>
<div>
<v-stage ref="stage" :config="stageSize" #click="changeOrder">
<v-layer>
<v-text :config="{text: 'Click me to change order', fontSize: 15}"/>
<v-rect v-for="item in items" v-bind:key="item.id" :config="item"/>
</v-layer>
<v-layer ref="dragLayer"></v-layer>
</v-stage>
</div>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height
},
items: [
{ id: 1, x: 10, y: 50, width: 100, height: 100, fill: "red" },
{ id: 2, x: 50, y: 70, width: 100, height: 100, fill: "blue" }
]
};
},
methods: {
changeOrder() {
const first = this.items[0];
// remove first item:
this.items.splice(0, 1);
// add it to the top:
this.items.push(first);
}
}
};
</script>
DEmo: https://codesandbox.io/s/vue-konva-list-render-l70vs?file=/src/App.vue

Konvajs - how to take a screenshot without transparency

Im just trying to let my user take a screenshot using the toDataUrl() function.
but, without a background rectangle, all pixel are transparent , and appear black .
so the solution is to dynamicly add a rectangle, generate the image, destroy the rectangle
saveImage(){
const stage=this.$parent.$refs.stage.getStage()
var stageRect = new Konva.Rect({
x:0,
y:0,
width: stage.attrs.width,
height: stage.attrs.height,
fill: 'green',
})
console.log(stage)
const backg=new Konva.Layer();
backg.add(stageRect)
stage.add(backg)
backg.setZIndex(0)
const dataURL = stage.toDataURL({ pixelRatio: 1, mimeType:"image/png" });
backg.destroy();
this.downloadURI(dataURL, 'stage.png');
},
it works (rectangle is created before all other layer) but... i can't get the size of the stage, i mean, the viewport because the user can zoom/dezoom the stage ....
any idea ?
Just use a scale to calculate background properties:
var stageRect = new Konva.Rect({({
x: -stage.x()/ stage.scaleX(),
y: -stage.y()/ stage.scaleY(),
width: stage.width() / stage.scaleX(),
height: stage.height() / stage.scaleY(),
fill: 'green',
});
Demo: https://jsbin.com/lehasitaje/2/edit?html,js,output

Is there a way to set negative/Inverted color as stroke/fill color

I'm drawing a closed line over a busy image as a background. It's working fine.
But my goal is to highlight the stroke as much as possible. Therefore, I'm looking for a way to make the line's stroke color 'negative/inverted' pixel of what's underneath that line.
For example:
<Line ... stroke="inverted" />
Possible? Achievable in some other way?
You may be able to use the globalCompositionOperation.
Mozilla docs here
Konva example here - see the parameters for the Konva.Rect() call. The example is for text, so not exactly as you require but hopefully you can adapt for the line requirement.
I tried to create a snippet to illustrate this but could not get exactly what you require. However, you can see how to apply the globalCompositionOperation parameter for a Konva line. Change the value of the comp variable to the other composition mode names from the Mozilla page to see their effects. The line is draggable.
An alternative may be to get the canvas pixel data, work out the pixels under the line and invert them individually.
// This is the color-composition mode
var comp = 'exclusion'
var stage = new Konva.Stage({
container: 'container',
width: 500,
height: 230
});
var layer = new Konva.Layer();
stage.add(layer)
var rect = new Konva.Rect({ x: 10, y: 0, width:500, height: 230,
fillLinearGradientStartPoint: { x: -50, y: -50 },
fillLinearGradientEndPoint: { x: 250, y: 150 },
fillLinearGradientColorStops: [0, 'red', 1, 'yellow']
})
layer.add(rect)
var ln = new Konva.Line({
points: [25, 70, 300, 20],
stroke: 'red',
strokeWidth: 15,
lineCap: 'round',
lineJoin: 'round',
draggable: true,
globalCompositeOperation: comp
});
layer.add(ln);
layer.draw()
stage.draw()
#container {
width: 500px;
height: 230px;
background-color: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/konva/3.2.5/konva.js"></script>
<div id="container"></div>

Konvajs shape stroke color lingers after change

I'm trying to change the stroke color of a hexagon on mouseover, and then back to the original color on mouseout.
My problem is that, if I redraw only the hexagon after updating the stroke color, the previous color lingers around the edges of the stroke.
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
});
hexagon.on('mouseout', function(e) {
e.target.stroke('gray');
e.target.draw();
});
Demo at https://codepen.io/jsgarvin/pen/dmRJXj
Here the original color is gray, and it changes to red on mouse over, but on mouse out it changes back to gray with a red dusting around all of the edges.
If I redraw the entire layer though, it seems to do what I expect, but in my particular use case I expect to have several thousand hexagons, among other things, on the layer, and that seems inefficient to redraw the entire layer if I just need to update one hexagon. Is there a more correct way to do this that I'm overlooking? Thanks!
You need to draw the layer.
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
I found this out as I was coding an alternative which I include below in the snippet. It uses a second shape and we show & hide the two so as to provide the mouseover effect. I can imagine that this will not be viable in all cases but it might help someone out so here is a working snippet.
Left hand is your example copied from your Pen with the fix included, the right is the shape switcher, just for fun.
var stage = new Konva.Stage({
container: 'container',
width: 400,
height: 150
});
var layer = new Konva.Layer();
stage.add(layer);
var c = layer.getCanvas();
var ctx = c.getContext();
var hexagon = new Konva.RegularPolygon({
x: 75,
y: 75,
radius: 55,
sides: 6,
stroke: 'gray',
strokeWidth: 10
});
hexagon.on('mouseover', function(e) {
e.target.stroke('red');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
hexagon.on('mouseout', function(e) {
e.target.stroke('gray');
e.target.draw();
layer.draw(); // <<<<< THIS LINE IS THE FIX
});
var hexagon2 = new Konva.RegularPolygon({
x: 250,
y: 75,
radius: 55,
sides: 6,
stroke: 'gray',
strokeWidth: 10
});
hexagon2.on('mouseover', function(e) {
e.target.visible(false);
hexagon3.visible(true);
layer.draw();
});
var hexagon3 = new Konva.RegularPolygon({
x: 250,
y: 75,
radius: 55,
sides: 6,
stroke: 'red',
strokeWidth: 10,
visible: false
});
hexagon3.on('mouseout', function(e) {
e.target.visible(false);
hexagon2.visible(true);
layer.draw();
});
layer.add(hexagon);
layer.add(hexagon2);
layer.add(hexagon3);
stage.draw();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/konvajs/konva/1.6.5/konva.min.js"></script>
<p>Left image is OP's version, right is shape-switching. Mouse-over the hexagons.</p>
<div id='container'></div>

Align labels on opposite sides of Highcharts container (donut graph)

I have a project using Highcharts 5 . I'm trying to get the chart labels on opposite sides of the donut graph, aligned straight left and straight right on each side, with the connectors spanning the distance appropriately.
Attached is the comp of what it should look like. Any thoughts on how to achieve this?
Thanks!
You can create the legend manually (html label and connector line with dot) using renderer:
var points = this.series[0].points,
renderer = this.renderer;
points.forEach(function(point) {
var labelOptions = point.options.label;
if (labelOptions) {
// text
renderer.text(labelOptions.text, labelOptions.x, labelOptions.y - 30, true).add();
// connector
var path = ['M', labelOptions.x, labelOptions.y].concat(labelOptions.connector.path);
renderer.path(path).attr({
stroke: 'gray',
'stroke-width': 1,
}).add();
// dot
renderer.circle(labelOptions.x, labelOptions.y, 3).attr({
fill: 'gray',
'stroke-width': 1,
}).add();
}
});
Options:
{
name: 'Microsoft Internet Explorer',
y: 56.33,
label: {
x: 400,
y: 100,
connector: {
path: ['l', -50, 0, 'l', 0, 100]
},
text: "<div class='label'><img src='https://wordpress.highcharts.com/blog/wp-content/uploads/2017/08/28160952/highcharts_logo.png' width='30' height='30'></img><div>Label text in HTML</div></div>"
}
}
Live working example (one label created): http://jsfiddle.net/kkulig/wdtpyof1/
This demo is only a good starting point. You should create your own mechanisms responsible for positioning labels.
API reference: http://api.highcharts.com/class-reference/Highcharts.SVGRenderer
This is solution for your issue
you can customize the connectors and get the chart labels on opposite sides of the donut graph.
http://jsfiddle.net/nmtri1101/ogfbxfqd/2/
Code jsfiddle :)

Resources