background

As the result of the OpenHarmony component development competition was announced, our team members were informed that they had won the second prize. We were so happy that we wanted to share our valuable development experience during this period with everyone. When we saw the notice of the competition, it was already mid-September, and it was time to submit our works. After referring to some other works, we found that there was no component developed based on Canvas at present, so we began to plan to write a component developed based on Canvas and general component. Since we had not developed OpenHarmony application before, none of our team members had relevant experience, so we explored from scratch. We first divided our work, with some members responsible for downloading the IDE and debugging the device, while others were responsible for researching and reading the official documentation. First attached source code:

Github.com/Wscats/open…

configuration

After reading the official documents, our members did the following environment configuration on their local computers and devices:

  1. Download and install DevEco Studio 2.1 Release and above
  2. Get the OpenHarmony SDK package and unpack it
  3. configurationOpenHarmony SDK

On the DevEco home screen, click File > Settings > Appearance & Behavior > System Settings > HarmonyOS SDK Click HarmonyOS SDK Location to load the SDK:

Then keep clicking Next and Finish to complete the environment configuration.

  1. Install additional packages, enterOpenHarmony - SDK - 2.0 - Canary/js / 2.2.0.0 / build - the tools/ace - loaderDirectory, run the cli tool in the directory, and run the following commands respectively until the installation is complete
npm cache clean -f
npm install
Copy the code
  1. downloadOpenHarmonyJSDemosProject engineering, import projectDevEco Studio
  2. Apply for and configure a certificateOpenHarmonyHarmonyOSCertificate is not universal, so additional application is required
  3. Do a compilation build to generate oneHAPApplication installation package, generateHAPApply the installation package and install toOpenHarmonyDevelopment board
  4. After installation and operation, click the application icon on the development board screen to open the application, and you can view the application example running effect on the device and conduct relevant debugging
  5. In addition to using real machine debugging, we can also use remote debugging and localPreviewerDebugging, although very convenient, but the actual performance and the real machine is certainly slightly different

preface

Before implementing Canvas application, we had some discussions and discussions. Firstly, we hoped to improve our understanding of OpenHarmony through this development, so as to facilitate the support of subsequent business. Secondly, our team members also hoped to get better ranking and awards. The full score is 100, and the work will be graded according to its creativity, practicability, user experience and code specification. The implementation cost of Canvas application is slightly more difficult than that of ordinary application, and it is difficult to debug, so we have little advantage in creativity and practicability. Because most of the front-end developers access to the Canvas applications are related to the game so that road is destined to be relatively difficult, user experience is a big difficulty, the performance of the real machine test, we find that Canvas also is not very good, far worse than the original experience of some components, for code quality of team members are confident, However, the code specification was the least weighted, so we had a big disagreement when we set up the project.

Selection of the dimension instructions score
creative How innovative the work is 30%
practical The actual application degree of the work in the application scene 30%
The user experience User experience value. Users can easily use components and get a good experience 25%
Code specification The quality of the code, aesthetics, compliance with the code 15%

plan

Because of these concerns, we have made three plans and one goal:

  • Implement common components using base components and container components – OpenHarmonyGallery
  • Using the canvas componentCanvasGame -OpenHarmonyFlappyBird
  • Implemented using base components, container components, and canvas componentsCanvasRendering engine -OpenHarmonySheet

The rendering engine was our ultimate goal, and although it was a bit of a challenge, our team members decided to do it in three steps: learn to use at least the base components and container components, then learn to use the canvas components, and finally implement a rendering engine by combining that experience.

Early experience

We first implemented a generic gallery component as a hands-on project, which uses four basic components and container components:

We place a button that triggers the showGallery method, which controls the display and hiding of panel pop-ups. The div and button tags are HML’s built-in components, much like we normally write HTML, and support most of our normal attributes like ID. Class and type are used to set the basic identity and appearance of components.

<div class="btn-div">
  <button type="capsule" value="Click Here" onclick="showGallery"></button>
</div>
Copy the code

Then we place the changeable gallery content display window in the panel component, and change mode and SRC into settable variables, so that the gallery component can make the gallery component display different shapes according to the mode, and display different picture contents according to the passed picture address. The syntax here is very similar to wechat applet and Vue framework. You can use Mustache syntax to control attribute values.

<panel
  id="gallery"
  class="gallery"
  type="foldable"
  mode="{{modeFlag}}}"
  onsizechange="changeMode"
>
  <div class="panel-div" onclick="closeGallery">
    <image class="panel-image" onclick="closeGallery" src="{{galleryUrl}}}"></image>
    <button
      class="panel-circle"
      onclick="closeGallery"
      type="circle"
      icon="/common/images/close.svg"
    ></button>
  </div>
</panel>
Copy the code

After implementing the view and layout, we can add the logic of the gallery component to the index.js directory of the same level. Thanks to the support of ES6 syntax, we can also write comfortably and efficiently. Here, data is the data model of the gallery component, and the type can be object or function. Note that property names cannot start with $or _, and do not use reserved words. Here we set default values for modeFlag and galleryUrl.

export default {
  data: {
    modeFlag: "full".galleryUrl:
      "https://pic1.zhimg.com/v2-3be05963f5f3753a8cb75b6692154d4a_1440w.jpg?source=172ae18b",}};Copy the code

The display and hide logic is relatively simple, just need to get the node of the panel, and then trigger the show or hide method, of course, in addition to this method, we can also use the render attribute to achieve:

  • forExpands the current element based on the set data list
  • ifAccording to the SettingsbooleanValue to add or remove the current element
  • showAccording to the SettingsbooleanValue to show or hide the current element
showGallery(e) {
    this.$element('gallery').show()
},
closeGallery(e) {
    if(e.target.type==='image') return
    this.$element('gallery').close()
},
Copy the code

We can also add styles to the index.css component in the same directory to make our gallery look better. Here the animation styles also support dynamic rotation, pan, zoom and gradient effects, all of which can be set in Style or CSS.

.panel-div {
  width: 100%;
  height: 100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
Copy the code

The overall implementation effect is shown in the picture below. The effect is simple and crude. After writing this DEMO, our team members have a basic understanding of the basic components of OpenHarmony:







The advanced

Although we have mastered the basics, we still haven’t used the Canvas Canvas component, so we went back to the official documentation and found that OpenHarmony provides a complete Canvas interface:

We used the classic FlappyBird game as our first attempt at a canvas component.

To collect material

First we prepare the game’s pictures and animations:

material
background
The bird
The ground
Water pipe

Then we prepare the canvas, set the height and width, and listen for the method onTouchEnd that the canvas presses.

<div class="container">
  <canvas ref="canvas" style="width: 280px; height: 512px;" ontouchend="moveUp"></canvas>
</div>
Copy the code

Data initialization

Once the canvas is ready, we need to initialize the game’s initial data, which mainly involves the following:

el The canvas element
gap Pipe spacing
score score
bX The x-coordinate of the bird
bY The y-coordinate of the bird
gravity Gravity index
pipe Pipeline data
birdHeight The bird height
birdWidth The bird width
pipeNorthHeight Upper pipe height
pipeNorthWidth Lower pipe height
cvsHeight Height of the canvas
cvsWidth Width of the canvas
fgHeight The height
fgWidth The width of the ground

Implementation before the game, we not only need to master the basic components, also need to know what part of the life cycle, there are two kinds of OpenHarmony life cycles, respectively is the application of life cycle and the page life cycle, we are here for the first time applied to onShow life cycle, it is in the page opens the trigger, and the application at the front desk when triggered, We need it to initialize some key data at the beginning, get the nodes of the canvas, save the context-scoped CTX of the canvas, clean up the pipeline data and trigger the game frame drawing.

onShow() {
    this.el = this.$refs.canvas;
    this.ctx = this.el.getContext('2d');
    this.pipe[0] = {
        x: this.cvsWidth,
        y: 0}; requestAnimationFrame(this.draw);
},
Copy the code

The this.draw method here is the core logic of the whole game, involving the bird’s flight animation, movement trajectory, boundary processing and score calculation.

We start by drawing the background of the game from the top left corner of the canvas, where the X and Y axes start.

const ctx = this.ctx;
ctx.drawImage(this.bg, 0.0);
Copy the code

Then we draw the pipe that appears in the sky and the ground during the bird’s flight. The position of the pipe in the sky needs to be calculated. The preset distance between the two pipes and the height of the pipe below need to be used to calculate the position. Just use a timer or requestAnimationFrame to update the location of pipes and birds in real time to give the user a sense of the game’s dynamic graphics. Here I used requestAnimationFrame to request animation frames for a better experience. However, it is only supported from API Version 6 and does not require you to import it, so readers need to be aware of whether your SDK is a newer Version.

for (let i = 0; i < this.pipe.length; i++) {
  this.constant = this.pipeNorthHeight + this.gap;
  ctx.drawImage(this.pipeNorth, this.pipe[i].x, this.pipe[i].y);
  ctx.drawImage(this.pipeSouth, this.pipe[i].x, this.pipe[i].y + this.constant);
  this.pipe[i].x--;
}
Copy the code

Collision detection

Here we use a boundary condition judgment to do deal with the collision detection, which is a bird if run into the ground, run into the sky of the pipe or pipe on the ground will make all the animation to stop, at the end of the game, if the game over the settlement results, and use OpenHarmony built-in popup window whether remind players need to start a new game.

if((this.bX + this.birdWidth >= this.pipe[i].x &&
    this.bX <= this.pipe[i].x + this.pipeNorthWidth &&
    (this.bY <= this.pipe[i].y + this.pipeNorthHeight ||
      this.bY + this.birdHeight >= this.pipe[i].y + this.constant)) ||
  this.bY + this.birdHeight >= this.cvsHeight - this.fgHeight
) {
  prompt.showDialog({
    buttons: [{ text: "Do it again."}].success: (data) = > this.restart(),
  });
  clearInterval(this.interval);
}
Copy the code

When processing the boundary, we also need to deal with when the bird flew down to constantly create new pipelines, recycling old pipeline score points, this logic is very simple, in essence is a kind of collision detection, when the pipeline position change to the picture on the left side of the X axis for the position of the five, namely birds have security through, has successfully score, When the latest pipe is changed to the position of 125 on X-axis on the right side of the picture, that is, the next pipe that the bird will fly over, the next new pipe should be created in advance. If the bird flies over a long distance, we also need to consider optimizing the pipe array, so that the array cannot grow indefinitely. We can also optimize, So when the old pipe has completely disappeared from the screen, we can consider deleting the old pipe’s data from the array.

if (this.pipe[i].x == 125) {
  this.pipe.push({
    x: this.cvsWidth,
    y: Math.floor(Math.random() * this.pipeNorthHeight) - this.pipeNorthHeight,
  });
}
if (this.pipe[i].x == 5) {
  this.score++;
}
Copy the code

All of this logic is essentially an animation of the drawing pipe. We can easily draw the bird’s flight path with offset and gravity factors, and we also draw the score in the bottom left corner of the screen to show the player’s score in real time.

ctx.drawImage(this.fg, 0.this.cvsHeight - this.fgHeight);
ctx.drawImage(this.bird, this.bX, this.bY);
this.bY += this.gravity;
ctx.fillStyle = "# 000";
ctx.font = "20px Verdana";
ctx.fillText("Score : " + this.score, 10.this.cvsHeight - 20);
Copy the code

Operation and scoring

We players only need one action to participate in the whole game, which is to click on the screen with your finger and try to make the bird fly safely between the pipes, so we need to listen for the click event on the screen, which is essentially the click event on the canvas, and when the user clicks, we ask the bird to move up a little distance.

moveUp() {
    this.bY -= 25;
},
Copy the code

Resetting the game is similar to the initialization logic, as long as the player score, bird position and pipe data are all restored to the default state:

restart() {
    this.pipe = [];
    this.pipe[0] = {
        x: this.cvsWidth,
        y: 0};this.constant = 0;
    this.score = 0;
    this.bY = 150;
},
Copy the code

Packaging components


Because the game requires us is to achieve a common component, so we want to go further in case 2, try to put the encapsulate the game into a generic components, found it very simple to implement, check the official documentation for details on custom components, is a custom component is user according to business needs, existing component combination, encapsulated into a new component, It can be called multiple times in the project to improve the readability of the code. To sum up, we just need to use the < Element > component to introduce the component we just implemented into the host page.

<element name="Flappy" src="./flappy//pages//index/index.hml"></element>
<div class="container">
  <Flappy></Flappy>
</div>
Copy the code

The ultimate challenge

With the accumulation of the previous two cases, our team had a clearer understanding of OpenHarmony development, and we were about to enter the final exciting challenge, which was to fully transplant a Canvas engine. We initially considered to implement a game engine, but considering that there was not enough time left in the competition, In addition, the practicality and creativity of the game engine are not conducive to presentation, so after comprehensive consideration of our team, we finally decided to implement a document table rendering engine.

thinking

Some people may wonder why they choose to transplant a document rendering engine. Here I recall a similar discussion on Zhihu. How long will it take China to develop an alternative software similar to Excel, with functions covering 95% of the functions of Excel? The road is rugged and difficult.

  • Microsoft Wheel: Can’t do it, so many things, it would take years to write requirements documentation. Microsoft’s Belleve: programmers can try to implement recalc (update cell values according to the formula) first, to know the difficulty, the documentation project as the most complex C++ project in the country by no means deserves its name.
  • Microsoft's monster brother: As an engineer of Excel, I answer a question seriously. I can’t, because our next group has tried, and it covers about 40% in two years.
  • The IBM Caspar Cui: WPS is a great substitute for developing common Excel features. And Microsoft and Golden hill also have cross – authorization. But to say that 95% of Excel already does that kind of thing… I’m still underestimating Excel. WPS has to work hard for just one help document.
  • University of Science and Technology of China Sixue: If Microsoft brainwashed, the Excel source lost, irreparable. It’s the end of the world. All together. Even if Microsoft brings back the original Excel team, rehires them, resurrects them from an untimely death, and starts all over again. He couldn’t guarantee that Excel would be 95% functional, that 95% of Excel files would be open.
  • Bbcallen: Impossible, Microsoft can’t do it themselves.

No matter how anyone says, this road we must go, just like the meaning behind the birth of Hongmeng, we choose to meet this challenge, here every obstacle every pit is worth leaving a Chinese footprint.

From technology and reason to go to the target Angle, we should realize not already cured the market, and the user habit of local personal documents but online collaborative documents, local documents need to consider personal, don’t need to consider many scenes together, only need to consider is offline, don’t need to consider online scene, only need to consider the client scenario, don’t need to worry about the server scenario, etc…

The host environment of online documents is the browser, and the system is behind the local documents. Behind any domestic online documents, there is no support like Google Document based on Google Browser, and there is no support based on Microsoft Office based on Microsoft Windows system. In fact, based on all this, we should also be aware that 95% is very difficult to achieve. We may not have such a technical background at home, but we are still trying to narrow the gap and keep up.

Implementation scheme

Before we talk about the implementation, let’s talk about how complex table rendering is. There are generally two implementations of table rendering:

  • DOMRendering.
  • CanvasRendering.

The well-known handsonTable open source library in the industry is to achieve rendering based on DOM. The same rendering result requires careful design and construction of DOM nodes, but it is obvious that DOM rendering of 100,000 or millions of cells will cause greater performance problems. Therefore, nowadays, many online forms are implemented based on Canvas and superimposed DOM. However, the implementation of Canvas requires consideration of visual area, scrolling operation and Canvas hierarchy, as well as some performance problems faced by Canvas itself, including how to direct Canvas, etc. High requirements for development, but in order to better user experience, more inclined to Canvas rendering implementation scheme.

Since most front-end project rendering layers are rendered layer by layer using frames according to the layout model tree structure, the entire rendering tree also corresponds to the layout model tree one by one. As a result, the entire render has many nodes. When the project is large, performance is greatly affected.

In order to improve the rendering performance, provide better editing experience from DOM to Canvas rendering, convenient for developers to build heavy front-end large online document projects, there are only a few companies at home and abroad to achieve similar engine, such as: Tencent Document, Kingsoft document and Google Document.

At the top
write DOM Container plug-in input box, etc
write Canvas Highlight selection, etc
write Canvas Content font background color, etc
The underlying

By collecting view elements by classification and rendering them by category, we can reduce The Times of state machine switching of Canvas drawing engine, reduce performance loss and optimize the rendering time. The whole core engine code is controlled at about 1500 lines, and 500 lines of demonstration code are added to facilitate people’s understanding, reading and secondary development.

The ported engine here mainly refers to a commercial project and an open source project:

  • X Spreadsheet@MyLiang
  • Tencent Doc@AlloyTeam

Canvas initialization

We construct a Table class, create the canvas at initialization, set the height and width, put it into the DOM, and mount the commonly used genera to the prototype chain and expose the global window variable.

class Table {
  constructor(container, width, height) {
    const target = document.createElement("canvas");
    document.querySelector(container).appendChild(target);
    this.$target = target;
    this.$draw = Canvas2d.create(target);
    this.$width = width;
    this.$height = height; }}Copy the code

We can instantiate the canvas directly in the global environment by placing the table element on the page at

, where the component identified by the ID attribute can be used to get the component object and call the related component method.

const table = new Table("#table".800.500);
Copy the code
Top left area Col column Col column
The row row The cell cell The cell cell
The row row The cell cell The cell cell

Coordinate system establishment

With canvas, we need to start preparing for rendering, we encapsulated the render method in the table class, render method mainly draw four areas, which is similar to the four imagination of the mathematical Cartesian coordinate system, involving grid line segments, grid information, column head (a-Z column), row head and frozen region.

2 – upper left region 1 – upper right region
3 – Lower left area 4 – Lower right area

These areas are initialized from area.js’s areas

  • Regions 2 through 1 are the a-Z column heads
  • Areas 2 through 3 are the row headers
  • The 4 fields are most commonly used and are editable cells
renderBody.call(this, draw, area4);
renderRowHeader.call(this, draw, iarea3);
renderColHeader.call(this, draw, iarea1);
renderFreezeLines.call(this, draw, area4.x, area4.y);
Copy the code

And the core idea of most of these four regions is essentially to draw grids, and all of them use the renderLinesAndCells method, which has methods for drawing lines and grids in the region, respectively. RenderCells inside iterate over the area and trigger renderCell to draw each individual cell, as well as special cells such as merged cells and selected cells, while renderLines iterate over each row and column to draw the spacing lines of all the rows.

function renderLinesAndCells() {
  renderLines(draw, area, lineStyle);
  renderCells(draw, type, area, cell, cellStyle, selection, selectionStyle, merges);
}
Copy the code

Cell rendering

After drawing the cells of the Table, we need to render data and format to each cell. Here we mount a cell method on the Table prototype chain, which takes a callback function and stores it on the static property $cell. This method is called when the renderCell function fires and passes the line number to the $cell method to get cell information.

Table.prototype["cell"] = function (callback) {
  this[`$$cell`] = callback;
  return this;
};
const sheet = [
  ["1"."1"."1"],
  ["1"."0"."1"],
  ["1"."1"."1"]]; table.cell((ri, ci) = >sheet? .[ri]? .[ci] ||"").render();
Copy the code

So we can use the exposed $cell method to control the text information and cell style of each cell. Of course, you can pass in plain text, or you can pass in complex data structure to decorate the cell. The style here will have the default value from $cellStyle.

bgcolor #ffffff
align left
valign middle
textwrap true
underline false
color #0a0a0a
bold false
italic false
rotate 0
fontSize 9
fontName Source Sans Pro

These styles are essentially displayed using the Drawing. Attr () interface provided by Canvas.

const c = cell(ri, ci);
let text = c.text || "";
let style = c.style;
cellRender(draw, text, cellRect, style);
Copy the code

With the most basic method above, we already have tabular data presentation capabilities. Now we can expose richer interfaces to third parties, such as commonly used merged cells. We call the merges method to tell the table class, Our cells in the range from G9 to H11 on the X-axis to B9 to D11 on the Y-axis are merged states,

Table.create("#table".800.500).merges(["G9:H11"."B9:D11"]);
Copy the code

RenderCells will then render the region’s cells in a special way, rendering it to the state of the merged cells.

eachRanges(merges, (it) = > {
  if(it.intersects(area)) { renderCell(draw, it.startRow, it.startCol, cell, area.rect(it), cellStyle); }});Copy the code

Editable data

In addition to cell merging, we also use the event handling of the canvas, because all of these methods just look at the state of the table, and the table is going to be edited by the user, so we have to listen for user clicks and input events, so we bind click, mousedown, Mousemove and Mouseup events, etc., can be monitored to display input boxes on the top of the canvas of corresponding cells, namely the DOM element on the Z-axis, to provide users with the function of input modification of cells.

bind($target, "click".(evt) = > {});
bind($target, "mousedown".(evt) = > {});
Copy the code

Therefore, in the DOM node, in addition to placing the

element, we can also arrange the

<div
  if="{{isShowArea}}"
  style="width: 100px; height: 25px; top: {{areaTop}}px; left: {{areaLeft}}px"
>
  <textarea
    if="{{isShowArea}}"
    focus="{{isFocus}}"
    value="{{content}}"
    selectchange="change"
    onchange="change"
  ></textarea>
</div>
Copy the code

With input box we can monitor user input operation, we put the input event binding on the textarea components when the component reaches event trigger condition, will perform in the JS corresponding event callback function, realize the page UI views and JS logic layer interactions, event callback function by parameters can carry additional information, Such as the data on the component object dataset event callback parameters, when the component the trigger event, the default event callback function will receive an event object, through the event object can get the corresponding information, we through the event object to get the user input values, and call the cell method to update the form inside the corresponding value in the cell, Of course, the actual situation is sometimes complicated, such as the user is changing the color of the cell text, so the data format is determined here.

textarea.addEventListener("input".(e) = > {
  let input = e.target.value;
  table.cell((ri, ci) = > {
    if (ri === row && ci === col) {
      return (window.data[ri][ci] =
        typeof value === "string" || typeof value === "number"? input : { ... value,text: input });
    }
    return window.data[ri][ci];
  });
});
Copy the code

The following figure shows the measured effect of our real machine. It can be seen that we have introduced the built-in library @System. prompt. Click the corresponding cell pop-up window to display the corresponding line and column information, which is convenient for us to develop and debug. Using the IDE’s built-in Previewer preview does not work, assuming that the KEYBOARD input event on the PC side was not triggered.

Toolbar implementation

Has the function of the basic view and edit the form, the next step, we can consider implementing toolbar toolbar, usually provide set ranks highly, text bold, centered, italics, underline and background color Settings, is actually the cell style method with ranks or scope information to encapsulate all kinds of interface implementation.

We’re going to look at a few common ones, colHeader, that can set your list header and its height, and if you don’t set it, it will have a default height.

table.colHeader({ height: 50.rows: 2 }).render();
Copy the code

In some cases, we may need to fix certain rows and columns in order to improve table readability. Freeze will help you freeze columns up to C6.

table.freeze("C6").render();
Copy the code

ScrollRows is usually used in conjunction with the frozen region, allowing the selection outside the frozen region to be scrolled.

table.scrollRows(2).scrollCols(1).render();
Copy the code

We can update the second row and second column of the cell to 8848 with red:

table
  .cell((ri, ci) = > {
    if (ri === 2 && ci === 2) {
      return {
        text: "8848".style: {
          color: "red",}}; }return this.sheet? .[ri]? .[ci] ||"";
  })
  .render();
Copy the code

Because there are too many types of cells that can be set, we will not expand them here. For details, we can refer to the following interface, which supports a variety of rich and varied changes. It can be seen that in fact, it is very similar to how we set CSS styles:

{
  cell: {
    text,
    style: {
      border, fontSize, fontName,
      bold, italic, color, bgcolor,
      align, valign, underline, strike,
      rotate, textwrap, padding,
    },
    type: text | button | link | checkbox | radio | list | progress | image | imageButton | date,
  }
}
Copy the code

Run OpenHarmonySheet, long press any cell to pop up a dialog box, and click the corresponding option to view the running results of common interfaces. This demonstration is for reference only, please refer to the documentation for more practical application scenarios:










Life cycles and events

After completing the above functions, we need to consider exposing the lifecycle and events and encapsulating them into a common component for access parties to use.

  • @sheet-showTable display
  • @sheet-hideForm hidden
  • @click-cell-startCell before clicking
  • @click-cell-endThe cell is clicked
  • @click-cell-longpressLong press form
  • @changeModify cell data

Because OpenHarmony provides a series of lifecycle callback methods for custom components, it is easy for developers to manage the internal logic of custom components. The life cycle includes onInit, onAttached, onDetached, onLayoutReady, onDestroy, onPageShow and onPageHide. Our table component can expose its own life cycle by taking advantage of various life cycle callbacks.

this.$emit("eventName", data);
Copy the code

The name attribute refers to the user-defined component name. The component name is case-insensitive and is in lower case by default. The SRC attribute indicates the HML file path of the component. If the name attribute is not set, the component name is the HML file name by default. The custom component uses onXXX or @xxx syntax to bind the child component event. The child component fires the event and transmits the value through this.$emit.

<element name="Sheet" src=".. /.. /components/index.hml"></element>
<Sheet
  sheet="{{sheet}}"
  @sheet-show="sheetShow"
  @sheet-hide="sheetHide"
  @click-cell-start="clickCellStart"
  @click-cell-end="clickCellEnd"
  @click-cell-longpress="clickCellLongpress"
  @change="change"
></Sheet>
Copy the code

We packaged all of the above together with the introduction and access documentation and uploaded it to the Giee-Open HarmonySheet repository to complete our form engine component.

Hard to review the whole process while challenging, but our team was solved of wisdom, in the whole course of the game our team also learn a lot about HongMeng, before has not been the opportunity to learn, through the chance of the game can rediscover HongMeng, also met some like-minded developers, shall make a summary, Works before entry to find out a direction, had better be yourself familiar with this field, and is not overlap with other entries, the work is responsible for myself, work is friendly to others, you also respect the game, after in the right direction to make every little plan and the final goal, well before the middle of the specific planning, steps across the ground is not too large, Can’t aim high, down-to-earth to accomplish every little plan, the process is for the team and myself are win-win, although we can not reach the destination, but looking back at each of our footprints all of our efforts to return to unity between team members, targeted to finish each task, is responsible earnestly, to help each other, make progress together, Just as What Hongmeng has experienced, a perfect system needs the concerted efforts of thousands of developers to build and polish it. We hope that there will be more and more good OpenHarmony open source projects. We will build our own ecology together by taking small steps to cover thousands of miles, small streams and rivers.

Finally, I sincerely hope OpenHarmony can develop stronger and smoother. Although this road is difficult, it is worthwhile.