This tutorial walks you through the process of building a new web component, whether it is an entirely new type of annotation interaction or an improvement of an already existing one.
A few key concepts before starting this tutorial:
Key concept | What's for? |
---|---|
npm | Tool to import external libraies required in the project and easily run build commands. |
package.json | Config settings for npm with the list of libraries the project needs to import. |
package-lock.json | File automatically generated by npm to keep track of exact version numbers. You will likely never need to go in here. |
node_modules/ | Folder where npm stores the external libraries. |
Typescript | Typed javascript language for easier coding. |
tsconfig.json | Config settings for Typescript and how the compiler should convert it into Javascript. |
Webpack | Tool to bundle the multiple libraries generated into one Javascript file. |
webpack.config.js | Config settings for WebPack. |
Some reading material:
- Article explaining the main Javascript concepts here.
In addition to classical javascript tools mentioned above, we use three amazing libraries in Pixano:
Library | What's for? |
---|---|
Lit Element | Easy building of Web Components. If you are not familiar with this tool, please follow their tutorial. |
PixiJS | Create rich, interactive 2d graphics elements. If you intend to edit the interactions on the canvas, please follow their tutorial. |
ThreeJS | Create 3d scenes. If you intend to edit the 3d scene, please follow their tutorial. |
Let's create a simple component that displays an image. A minimal setup to do so is as follows:
Create a new file pxn-my-element.ts
under the graphics-2d
package:
import { LitElement, html, customElement, property } from 'lit-element';
@customElement('pxn-my-element' as any)
export class MyElement extends LitElement {
// input image path
@property({type: String})
public path: string = '';
/**
* Render the element template.
*/
render() {
/**
* `render` must return a lit-html `TemplateResult`.
*
* To create a `TemplateResult`, tag a JavaScript template literal
* with the `html` helper function:
*/
return html`
<img src=${this.path}>
`;
}
}
- In the file 'demo/plugins_index.js':
- add the component name to the 'pluginsList'
- add a default annotation schema for it in 'defaultLabelValues'
- In the file 'demo/serverless-demo.js':
- add the component call in the 'plugin' function
- add the tools used in the component in the 'tools' function
- add the component in an adapted case (or create a new case if needed) in the handlers: 'onCreate', 'onDelete', 'onSelection', 'onUpdate', 'onAttributeChanged'
Create a simple demo application that imports and instanciates the component to check that it behaves as expected. Create a my-element
folder in demos with the following files:
demos
└── my-element
├── my-demo.js
├── image.jpg
├── index.html
└── webpack.config.js
- Create an entrypoint
index.html
file:
<!doctype html>
<html>
<head>
<title>My Demo</title>
</head>
<body>
<!-- Use Web Components in your HTML like regular built-in elements. -->
<my-demo></my-demo>
<script src="./my-demo-bundle.js"></script>
</body>
</html>
- Add its javascript code in a
my-demo.js
:
import { html, LitElement} from 'lit-element';
import '@pixano/graphics-2d/lib/pxn-my-element';
class MyDemo extends LitElement {
render() {
return html`<pxn-my-element path="image.jpg"></pxn-my-element>`;
}
}
customElements.define('my-demo', MyDemo);
- Create a
package.json
to define the dependencies of the demo:
{
"name": "demo-my-element",
"version": "0.5.0",
"private": true,
"description": "Demo",
"scripts": {
"build": "webpack --config webpack.config.js",
"watch": "webpack --config webpack.config.js --watch"
},
"devDependencies": {
"source-map-loader": "^0.2.4",
"webpack": "4.44.2",
"webpack-cli": "3.3.12"
},
"dependencies": {
"@pixano/graphics-2d": "0.5.0",
"@webcomponents/webcomponentsjs": "^2.4.3",
"lit-element": "^2.3.1"
}
}
- Create
webpack.config.js
to define the parameters of the app bundling:
const path = require('path');
module.exports = {
mode: 'development',
entry: path.resolve(__dirname, 'my-demo.js'),
output: {
path: path.resolve(__dirname),
filename: 'my-demo-bundle.js'
},
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
use: ["source-map-loader"],
enforce: "pre"
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js']
}
};
# This assumes you already have installed
# the roots dependancies (npm i)
npm run bootstrap
# Watch src file change
npm run watch
# In another terminal, serve the demo app:
npx serve demo
Let's create a component that inherits pxn-polygon
, adding a way to create a polygon from a click fed to a segmentation algorithm through a HTTP request.
Create a new file pxn-http-polygon.ts
under the graphics-2d
package:
import { LitElement, html, customElement, property } from 'lit-element';
import { Polygon } from "./pxn-polygon";
@customElement('pxn-http-polygon' as any)
export class HttpPolygon extends Polygon {
// segmentation url
@property({type: String})
public segurl: string = 'localhost:4000';
constructor() {
super();
this.shManager.setController('smart-create', new SmartPolygonCreateController(this.renderer, this.shapes));
}
}
/**
* Inherit RectanglesManager to handle smart rectangle creation.
*/
class SmartPolygonCreateController extends ShapeCreateController {
protected onRootDown(evt: any) {
const click = this.renderer.getPosition(evt.data);
fetch(this.segurl, {
method: "POST",
body: {click, image: this.renderer.imageBase64 }
}).then((res) => res.json())
.then((pts: number[]) => {
this.shapes.add(observable({
id: Math.random().toString(36),
geometry: { type: "polygon", vertices: pts }
}));
});
}
}
Go through the above from scratch demo. Edit url as you want.