Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/vue3 #133

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ export default {
</script>
```

### Strict mode

By default `vue-konva` works in "non-strict" mode. If you changed a property **manually** (or by user action like `drag&drop`) properties of the node will be not matched with properties passed as `config`. `vue-konva` updates ONLY changed properties.

In strict mode `vue-konva` will update all properties of the nodes to the values that you provided in `config`, no matter changed they or not.

You should decide what mode is better in your actual use case.

To enable strict mode pass __useStrictMode attribute:

```html
<v-rect :config="{}" __useStrictMode >
```


## Configurable prefix

By default `vue-konva` is using `v-` prefix for all components.
Expand Down
5,320 changes: 3,386 additions & 1,934 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "vue-konva",
"version": "2.1.6",
"version": "2.1.7",
"description": "Vue binding to canvas element via Konva framework",
"keywords": [
"vue",
Expand Down Expand Up @@ -53,13 +53,13 @@
"konva": ">3.2.5"
},
"devDependencies": {
"@vue/test-utils": "^1.0.0-beta.25",
"@vue/test-utils": "^2.0.0-beta.10",
"chai": "^4.2.0",
"konva": ">3.2.5",
"nwb": "^0.25.2",
"sinon": "^7.3.0",
"vue": "^2.6.11",
"vue-template-compiler": "^2.6.11"
"vue": "^3.0.11",
"@vue/compiler-sfc": "^3.0.4"
},
"engines": {
"node": ">= 4.0.0",
Expand Down
38 changes: 19 additions & 19 deletions src/components/KonvaNode.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { h } from 'vue';
import {
applyNodeProps,
findParentKonva,
createListener,
updatePicture,
konvaNodeMarker,
checkOrder,
} from '../utils';

const EVENTS_NAMESPACE = '.vue-konva-event';

const CONTAINERS = {
Expand All @@ -21,11 +20,11 @@ export default function (nameNode) {
// Mark it to detect whether an Vue instance is KonvaNode or not later
[konvaNodeMarker]: true,

render(createElement) {
render() {
// containers should be able to draw children
const isContainer = CONTAINERS[nameNode];
if (isContainer) {
return createElement('template', this.$slots.default);
return h('template', this.$slots.default?.());
}
// other elements are not containers
return null;
Expand All @@ -45,31 +44,34 @@ export default function (nameNode) {
return {};
},
},
__useStrictMode: {
type: Boolean,
},
},
created() {
this.initKonva();
},
mounted() {
const parentVueInstance = findParentKonva(this);
const parentKonvaNode = parentVueInstance._konvaNode;
parentKonvaNode.add(this._konvaNode);
updatePicture(this._konvaNode);
const parentKonvaNode = parentVueInstance.__konvaNode;
parentKonvaNode.add(this.__konvaNode);
updatePicture(this.__konvaNode);
},
updated() {
this.uploadKonva();
checkOrder(this.$vnode, this._konvaNode);
checkOrder(this.$, this.__konvaNode);
},
destroyed() {
updatePicture(this._konvaNode);
this._konvaNode.destroy();
this._konvaNode.off(EVENTS_NAMESPACE);
unmounted() {
updatePicture(this.__konvaNode);
this.__konvaNode.destroy();
this.__konvaNode.off(EVENTS_NAMESPACE);
},
methods: {
getNode() {
return this._konvaNode;
return this.__konvaNode;
},
getStage() {
return this._konvaNode;
return this.__konvaNode;
},
initKonva() {
const NodeClass = window.Konva[nameNode];
Expand All @@ -79,19 +81,17 @@ export default function (nameNode) {
return;
}

this._konvaNode = new NodeClass();
this._konvaNode.VueComponent = this;

this.__konvaNode = new NodeClass();
this.$.vnode.__konvaNode = this.__konvaNode;
this.uploadKonva();
},
uploadKonva() {
const oldProps = this.oldProps || {};
const props = {
...this.$attrs,
...this.config,
...createListener(this.$listeners),
};
applyNodeProps(this, props, oldProps);
applyNodeProps(this, props, oldProps, this.__useStrictMode);
this.oldProps = props;
},
},
Expand Down
33 changes: 17 additions & 16 deletions src/components/Stage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Vue from 'vue';
import { applyNodeProps, createListener, checkOrder } from '../utils';
import { h } from 'vue';
import { applyNodeProps, checkOrder } from '../utils';

export default Vue.component('v-stage', {
render: function (createElement) {
return createElement('div', this.$slots.default);
export default {
render: function () {
return h('div', this.$slots.default?.());
},
watch: {
config: {
Expand All @@ -20,10 +20,12 @@ export default Vue.component('v-stage', {
return {};
},
},
__useStrictMode: {
type: Boolean,
},
},

created() {
this._konvaNode = new window.Konva.Stage({
this.__konvaNode = new window.Konva.Stage({
width: this.config.width,
height: this.config.height,
// create fake container, later it will be replaced with real div on the page
Expand All @@ -32,33 +34,32 @@ export default Vue.component('v-stage', {
},
mounted() {
this.$el.innerHTML = '';
this._konvaNode.container(this.$el);
this.__konvaNode.container(this.$el);
this.uploadKonva();
this.validateChildren();
},
updated() {
this.uploadKonva();
this.uploadKonva();
checkOrder(this.$vnode, this._konvaNode);
checkOrder(this.$, this.__konvaNode);
},
beforeDestroy() {
this._konvaNode.destroy();
beforeUnmount() {
this.__konvaNode.destroy();
},
methods: {
getNode() {
return this._konvaNode;
return this.__konvaNode;
},
getStage() {
return this._konvaNode;
return this.__konvaNode;
},
uploadKonva() {
const oldProps = this.oldProps || {};
const props = {
...this.$attrs,
...this.config,
...createListener(this.$listeners),
};
applyNodeProps(this, props, oldProps);
applyNodeProps(this, props, oldProps, this.__useStrictMode);
this.oldProps = props;
},
validateChildren() {
Expand All @@ -68,4 +69,4 @@ export default Vue.component('v-stage', {
// })
},
},
});
};
10 changes: 3 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,15 @@ const components = [
];

const VueKonva = {
install: (Vue, options) => {
install: (app, options) => {
let prefixToUse = componentPrefix;
if(options && options.prefix){
if (options && options.prefix) {
prefixToUse = options.prefix;
}
components.forEach(k => {
Vue.component(`${prefixToUse}${k.name}`, k.component);
app.component(`${prefixToUse}${k.name}`, k.component);
})
}
};

export default VueKonva;

if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(VueKonva);
}
11 changes: 8 additions & 3 deletions src/utils/applyNodeProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const EVENTS_NAMESPACE = '.vue-konva-event';
export default function applyNodeProps(
vueComponent,
props = {},
oldProps = {}
oldProps = {},
useStrict
) {
const instance = vueComponent._konvaNode;
const instance = vueComponent.__konvaNode;
var updatedProps = {};
var hasUpdates = false;
for (let key in oldProps) {
Expand Down Expand Up @@ -53,7 +54,11 @@ export default function applyNodeProps(
instance.on(eventName + EVENTS_NAMESPACE, props[key]);
}
}
if (!isEvent && props[key] !== oldProps[key]) {
if (
!isEvent &&
(props[key] !== oldProps[key] ||
(useStrict && props[key] !== instance.getAttr(key)))
) {
hasUpdates = true;
updatedProps[key] = props[key];
}
Expand Down
49 changes: 24 additions & 25 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,9 @@ export function copy(obj) {
return JSON.parse(JSON.stringify(obj));
}

export function createListener(obj) {
const output = {};
Object.keys(obj).forEach((eventName) => {
output['on' + eventName] = obj[eventName];
});
return output;
}

export function findParentKonva(instance) {
function re(instance) {
if (instance._konvaNode) {
if (instance.__konvaNode) {
return instance;
}
if (instance.$parent) {
Expand All @@ -30,37 +22,44 @@ export function findParentKonva(instance) {
}

export function findKonvaNode(instance) {
if (!instance) {
if (!instance?.component?.ctx) {
return null;
}
if (instance.$options[konvaNodeMarker]) {
return instance.getNode();
if (instance?.component?.ctx?.__konvaNode) {
return instance.component.ctx.__konvaNode;
}
if (instance.$children.length === 0) {
return null;
if (instance.component.subTree.__konvaNode) {
return instance.component.subTree.__konvaNode
}
return findKonvaNode(instance.$children[0]);
return findKonvaNode(instance.component.subTree)
}

export function checkOrder($vnode, konvaNode) {
export function checkOrder($, konvaNode) {
let needRedraw = false;
// check indexes
// somehow this.$children are not ordered correctly
// so we have to dive-in into componentOptions of vnode
// also componentOptions.children may have empty nodes, and other non Konva elements so we need to filter them first

const children = $vnode.componentOptions.children || [];
let children = [];

if ($.subTree.children) {
$.subTree.children.forEach((child) => {
if (!child.component && Array.isArray(child.children)) {
children.push(...child.children);
}
if (child.component) {
children.push(child);
}
});
}

const nodes = [];
children.forEach(($vnode) => {
const konvaNode = findKonvaNode($vnode.componentInstance);
const konvaNode = findKonvaNode($vnode);
if (konvaNode) {
nodes.push(konvaNode);
}

const { elm, componentInstance } = $vnode;
if (elm && elm.tagName && componentInstance && !konvaNode) {
const name = elm && elm.tagName.toLowerCase();
const { el, component } = $vnode;
if (el && el.tagName && component && !konvaNode) {
const name = el && el.tagName.toLowerCase();
console.error(
`vue-konva error: You are trying to render "${name}" inside your component tree. Looks like it is not a Konva node. You can render only Konva components inside the Stage.`
);
Expand Down
Loading