preface
This time to share a self using Canvas draw a configurable scale (ruler) component. Component application: It is mainly used for sliding selection of value and amount on mobile terminal to enhance user interactive experience.
This paper deals with development ideas, code disassembly, problem solving, calibration component library encapsulation and many other aspects of knowledge. The children’s shoes that have need can consult appropriately, develop more flowery, more perfect component.
Welcome to Github. Start: github.com/now1then/ca… ;
Also welcome to the demo address experience function: rnlvwyx.cn:3333/#/demo;
Description of functional analysis
Scale component Demo, effect drawing:
Component development will accomplish the following functions:
- Canvas drawing component is adopted to solve the problem of blurred drawing on mobile terminal.
- Support scale basic parameter configuration in,
- Monitor the sliding event, and output the scale value in real time when sliding. At the same time, the scale can be dynamically set according to the external value.
- Support smooth/slow sliding, real-time scale rendering,
- Compatible with mobile/PC slide,
Components use
Import the scale.js file:
import scale from './scale.js'; // Import the scale.js file
// or
npm install canvas-scale
import scale from 'canvas-scale';
Copy the code
The Scale module exposes an init() initialization method; Scale. The init () function:
- The first parameter is passable
document.querySelector()
Gets the HTML node; - The second parameter is the configuration item to be reset.
- The third parameter is passed in the callback function when the scale is changed, which can be used to get the latest scale value.
- Returns an instance object that exposes some operation methods.
/** * scale Function * @param {String} EL HTML node * @param {Object} Options configuration information * @param {Function} callBack callBack Function * @returns { Object} */
// Draw the scale
const myScale = scale.init('#myScale', {height: 50.start: 10000.end: 2000}, callBack);function callBack(value) {
console.log(value);
}
Copy the code
Currently the returned instance object exposes the following methods:
update(value)
: Updates the canvas display by passing in the latest scale value.Value: indicates the latest scale value
clear()
: Clears the current canvas.resize(option)
: resets the canvas. You can pass in the latest configuration information to be reset.Option: Indicates the scale configuration
myScale.update(1000); // Update the scale value
myScale.clear(); // Clear the canvas
myScale.resize(); // Reset the scale canvas
Copy the code
The development of
Children who are not familiar with Canvas API can visit Canvas API Chinese website to learn. I have summarized the article on Canvas development and frequently asked questions:
Here is to explain the development idea, mainly divided into the following steps:
- Configuration items and Functions:
- Canvas to draw:
- Draw center line
- Draw the entire scale
- Cut the scale according to the actual scale value
- Draw signature and background
- Interaction:
- Added listening for slide events to draw scale canvas in real time
- The value changes in conjunction with the scale
- All kinds of boundary exception handling
Configuration items
The configuration items supported by the component are as follows:
// Default configuration
const default_conf = {
// < span style = "box-sizing: border-box; color: RGB (255, 255, 255)
height: 50.// Canvas height
start: 1000.// Start value of the scale
end: 10000.// End of scale value
// def: 100, // the calibration value of the center line
unit: 10.// Scale interval 'px'
capacity: 100.// Scale capacity value
background: '#fff'.// Set the color of the background to the corresponding color unreal effect, do not set the default is all white.
lineColor: '#087af7'.// Centerline color
openUnitChange: true.// Whether to enable interval scale change
sign: '@nowThen'.// The signature is not displayed
fontColor: '#68ca68'.// The value color of the scale is not set yet
fontSize: '16px SimSun, Songti SC'.// Scale numeric font style
};
Copy the code
Pass the configuration item through the second argument of scale.init(), otherwise the default configuration item above is used.
Canvas to draw
Draw the canvas based on the passed container:
// Draw a canvas based on the passed container
const container = document.querySelector(el);
container.appendChild(canvas);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
Copy the code
Draw center line
The midline stays in the middle of the canvas, identifying the current scale value.
// Draw the center line
function drawMidLine() {
const mid_x = Math.floor(config.width / 2);
ctx.beginPath();
ctx.fillStyle = config.lineColor;
ctx.fillRect(mid_x - 1.0.2, config.height);
ctx.stroke();
ctx.moveTo(mid_x, 8);
ctx.lineTo(mid_x - 5.2);
ctx.lineTo(mid_x - 5.0);
ctx.lineTo(mid_x + 5.0);
ctx.lineTo(mid_x + 5.2);
ctx.fill();
ctx.moveTo(mid_x, config.height - 8);
ctx.lineTo(mid_x - 5, config.height - 2);
ctx.lineTo(mid_x - 5, config.height);
ctx.lineTo(mid_x + 5, config.height);
ctx.lineTo(mid_x + 5, config.height - 2);
ctx.fill();
ctx.closePath();
}
Copy the code
Set the signature and background color
Background color config.background: The background is the color value passed in and gives the canvas an extra illusion of the scale lines on both sides of the canvas. Pass empty, then the entire canvas is transparent color, and no two ends of the blur effect.
The signature config.sign is displayed in the upper right corner of the canvas. If a null value is passed in, the signature is not displayed.
// Set the signature and background
function drawSign() {
// Background blur effect,,
if (config.background) {
ctx.beginPath();
var gradient1 = ctx.createLinearGradient(0.0, config.width, 0);
gradient1.addColorStop(0.'rgba (255, 255, 255, 0.95)');
gradient1.addColorStop(0.45.'rgba(255, 255, 255, 0)');
gradient1.addColorStop(0.55.'rgba(255, 255, 255, 0)');
gradient1.addColorStop(1.'rgba (255, 255, 255, 0.95)');
ctx.fillStyle = gradient1;
ctx.fillRect(0.0, config.width, config.height);
ctx.closePath();
}
/ / signature
if (config.sign) {
ctx.beginPath();
ctx.font = '10px Arial';
var gradient = ctx.createLinearGradient(config.width, 0, config.width - 50.0);
gradient.addColorStop(0.'rgba (255, 0, 0, 0.3)');
gradient.addColorStop(1.'rgba (0, 128, 0, 0.3)');
ctx.fillStyle = gradient;
ctx.textAlign = 'right';
ctx.fillText(config.sign, config.width - 10.10);
ctx.closePath();
ctx.fillStyle = 'transparent'; }}// Set the background value when drawing the scale
ctx_bg.fillStyle = _config.background || 'transparent'; / / the background color
Copy the code
Draw the scale
There are two possible ideas:
- The whole scale canvas is drawn in advance. When sliding, a part of the scale canvas is cut according to the parameters and drawn into the visible area.
- According to the current scale value, sliding distance and other parameters, the real-time scale distribution of the visible area of the canvas is drawn.
In real development, both schemes were tried and implemented. There are also some problems:
The first option:
The steps are as follows:
- Set the style based on the incoming configuration; Calculate the width and height of the scale canvas;
- Draw the bottom line of the whole scale;
- Count the number of scales, and draw each scale in turn, scale width 1px, normal scale
1/5 * height
High, at intervals of 5 scales1/3 * height
High, at intervals of 10 scales1/2 * height
High and draw the scale value; - Calculate the starting position of the canvas of the cut ruler;
- GetImageData (sx, sy, sWidth, sHeight); Capture the scale image area by context.putimageData (imageData, 0, 0); Draws the data from the ImageData object captured above onto the main canvas.
The same effect can be achieved using context.drawImage().
When sliding, according to the sliding position, cut the corresponding ruler area graph and draw it on the main canvas.
// Create a new scale canvas as the underlying image
const canvas_bg = document.createElement('canvas');
const ctx_bg = canvas_bg.getContext('2d');
// Draw the scale
function drawScale() {
const mid = config.end - config.start + 1; // Value range
const scale_len = Math.ceil(mid / config.capacity); // The number of ticks
const space = Math.floor(config.width / 2); // The left and right sides of the gap, according to the value of the integer multiple scale value to draw the line
const beginNum = Math.ceil(config.start / config.capacity) * config.capacity;
const st = (Math.ceil(config.start / config.capacity) - config.start / config.capacity) * config.unit;
// Set the width and height of canvas_bg
canvas_bg.width = (config.unit * (scale_len - 1) + config.width) * dpr;
canvas_bg.height = config.height * dpr;
ctx_bg.scale(dpr, dpr);
ctx_bg.beginPath();
ctx_bg.fillStyle = config.background || 'transparent'; / / the background color
ctx_bg.fillRect(0.0, canvas_bg.width, config.height);
ctx_bg.closePath();
/ / the bottom line
ctx_bg.beginPath();
ctx_bg.moveTo(0, config.height);
ctx_bg.lineTo(canvas_bg.width, config.height);
ctx_bg.strokeStyle = config.scaleLineColor || '#9E9E9E';
ctx_bg.lineWidth = 1;
ctx_bg.stroke();
ctx_bg.closePath();
// Draw the scale line
for (let i = 0; i < scale_len; i++) {
ctx_bg.beginPath();
ctx_bg.strokeStyle = config.scaleLineColor || "#9E9E9E";
ctx_bg.font = config.fontSize;
ctx_bg.fillStyle = config.fontColor;
ctx_bg.textAlign = 'center';
ctx_bg.shadowBlur = 0;
const curPoint = i * config.unit + space + st;
const curNum = i * config.capacity + beginNum;
if (curNum % (config.capacity * 10) = = =0) {
ctx_bg.moveTo(curPoint, (config.height * 1) / 2);
ctx_bg.strokeStyle = config.scaleLineColor || "# 666";
ctx_bg.shadowColor = '#9e9e9e';
ctx_bg.shadowBlur = 1;
ctx_bg.fillText(
curNum,
curPoint,
(config.height * 1) / 3
);
} else if (curNum % (config.capacity * 5) = = =0) {
ctx_bg.moveTo(curPoint, (config.height * 2) / 3);
ctx_bg.strokeStyle = config.scaleLineColor || "# 888";
if (scale_len <= 10) {
ctx_bg.font = '12px Helvetica, Tahoma, Arial';
ctx_bg.fillText(
curNum,
curPoint,
(config.height * 1) / 2); }}else {
ctx_bg.moveTo(curPoint, (config.height * 4) / 5);
if (i === 0 || i === scale_len - 1) {
ctx_bg.font = '12px Helvetica, Tahoma, Arial';
ctx_bg.fillText(
curNum,
curPoint,
(config.height * 2) / 3
);
}
}
ctx_bg.lineTo(curPoint, config.height);
ctx_bg.stroke();
ctx_bg.closePath();
}
point_x = (config.def - config.start) / config.capacity * config.unit; // Initialize the start position
const imageData = ctx_bg.getImageData(point_x * dpr, 0, config.width * dpr, config.height * dpr)
ctx.putImageData(imageData, 0.0);
}
Copy the code
Second option:
The steps are as follows:
- Draw the bottom line of the whole scale;
- Calculate the leftmost scale based on the center scale;
- Calculate the number of scale lines that can be drawn in the canvas area and the position and value of the first scale line;
- Draw each scale in turn, scale width 1px, normal scale
1/5 * height
High, at intervals of 5 scales1/3 * height
High, at intervals of 10 scales1/2 * height
High and draw the scale value; - then
context.drawImage()
Draws the image to the main canvas area;
When sliding, calculate the current middle scale value in real time, and redraw the entire canvas according to the drawing steps above.
// Create a new scale canvas as the underlying image
const canvas_bg = document.createElement('canvas');
const ctx_bg = canvas_bg.getContext('2d');
// Draw the scale
function drawScale() {
// Set the width and height of canvas_bg
canvas_bg.width = (config.unit * (scale_len - 1) + config.width) * dpr;
canvas_bg.height = config.height * dpr;
ctx_bg.scale(dpr, dpr);
// Obtain the leftmost scale based on the midpoint scale
let begin_num = current_def - (config.width / 2) * (config.capacity / config.unit);
let cur_x = 0;
let cur_num = 0;
const scale_len = Math.ceil((config.width + 1) / config.unit); // The number of ticks
const real_len = Math.ceil((config.end - config.start + 1) / config.capacity); // The actual number of scales that can be drawn
ctx_bg.fillStyle = config.background || 'transparent'; / / the background color
ctx_bg.fillRect(0.0, config.width, config.height);
ctx_bg.closePath();
/ / the bottom line
ctx_bg.beginPath();
ctx_bg.moveTo(0, config.height);
ctx_bg.lineTo(config.width, config.height);
ctx_bg.strokeStyle = config.scaleLineColor || '#9E9E9E';
ctx_bg.lineWidth = 1;
ctx_bg.stroke();
ctx_bg.closePath();
let space_num = Math.ceil(begin_num / config.capacity) * config.capacity - begin_num;
let space_x = space_num * (config.unit / config.capacity);
// Draw the scale line
for (let i = 0; i < scale_len; i++) {
cur_num = (Math.ceil(begin_num / config.capacity) + i) * config.capacity;
if (cur_num < config.start) {
continue;
} else if (cur_num > config.end) {
break;
}
ctx_bg.beginPath();
ctx_bg.strokeStyle = config.scaleLineColor || "#9E9E9E";
ctx_bg.font = config.fontSize;
ctx_bg.fillStyle = config.fontColor;
ctx_bg.textAlign = 'center';
ctx_bg.shadowBlur = 0;
cur_x = space_x + i * config.unit;
if (cur_num % (config.capacity * 10) = = =0) {
ctx_bg.moveTo(cur_x, (config.height * 1) / 2);
ctx_bg.strokeStyle = config.scaleLineColor || "# 666";
ctx_bg.shadowColor = '#9e9e9e';
ctx_bg.shadowBlur = 1;
ctx_bg.fillText(
cur_num,
cur_x,
(config.height * 1) / 3
);
} else if (cur_num % (config.capacity * 5) = = =0) {
ctx_bg.moveTo(cur_x, (config.height * 2) / 3);
ctx_bg.strokeStyle = config.scaleLineColor || "# 888";
if (real_len <= 10) {
ctx_bg.font = '12px Helvetica, Tahoma, Arial';
ctx_bg.fillText(
cur_num,
cur_x,
(config.height * 1) / 2); }}else {
ctx_bg.moveTo(cur_x, (config.height * 4) / 5);
}
ctx_bg.lineTo(cur_x, config.height);
ctx_bg.stroke();
ctx_bg.closePath();
}
ctx.drawImage(canvas_bg, 0.0, config.width * dpr, config.height * dpr, 0.0, config.width, config.height);
}
Copy the code
conclusion
The first option: easier to implement; The scale canvas only needs to be drawn once, and there is no need to redraw the scale canvas when sliding; However, when the scale interval is drawn with a large span, the performance is not good, and the canvas size is too large, the problem of drawing blank will occur. The second scheme: it is difficult to locate the initial value and the end value of the visible area scale, and a slide, the entire canvas is recalculated for each drawing point. The implementation is more cumbersome at first glance, as the entire canvas has to be drawn in real time while sliding, but it’s much better in effect, performance, and compatibility than the fatal flaws of the first solution.
In this way, the initial scale style is generated.
Problems encountered during development (described in more detail later in this article) :
- The drawing of mobile terminal Canvas is blurred, and the image inserted by canvas is blurred;
- Context.drawimage () argument problem;
- When the canvas drawing size or the insert image size is larger than a certain threshold, the drawing blank problem may occur.
Sliding event listener, compatible with PC
Listen for left and right swipe events, get the distance of each finger swipe, calculate how far the scale needs to move, and redraw the canvas.
- Registration events are compatible with mobile and PC; The mobile side listens for touch events, and the PC side listens for mouse events. It is important to pay attention here to get the difference processing of the current touch point.
[0].pagex; The X coordinate of the mouse is e. Puex on the PC.
- In the move event, draw in real time, update the scale value in real time, and call the passed callback function to return the scale value;
- Handle the case of moving to the left and right sides of the boundary. When the set maximum and minimum values are reached, the movement cannot continue;
- using
window.requestAnimationFrame()
Optimize rendering frequency. - Before you redraw it, you need to use
ctx.clearRect()
Empty the canvas before drawing, otherwise the drawing overlaps. - According to configuration items
config.openUnitChange
Determine if you can only interval scale moves, such as 100, 200, 300… The changes. The incomingfalse
, change according to the actual moving distance.
The effect is as follows:
// Event interaction (first scenario)
function addEvent() {
let begin_x = 0; // Finger x coordinate
let ifMove = false; // Whether to start the interaction
let moveDistance = 0;
// Register events, mobile and PC
const hasTouch = 'ontouchstart' in window;
const startEvent = hasTouch ? 'touchstart' : 'mousedown';
const moveEvent = hasTouch ? 'touchmove' : 'mousemove';
const endEvent = hasTouch ? 'touchend' : 'mouseup';
canvas.addEventListener(startEvent, start);
canvas.addEventListener(moveEvent, move);
canvas.addEventListener(endEvent, end);
function start(e) {
e.stopPropagation();
e.preventDefault();
ifMove = true;
if(! e.touches) { begin_x = e.pageX; }else {
begin_x = e.touches[0].pageX; }}function move(e) {
e.stopPropagation();
e.preventDefault();
const current_x = e.touches ? e.touches[0].pageX : e.pageX;
if (ifMove) {
moveDistance = current_x - begin_x;
begin_x = current_x;
point_x = point_x - moveDistance; // The scale offset
const space = Math.floor(config.width / 2);
// Boundary value processing
if (point_x <= 0) {
point_x = 0;
} else if (point_x >= canvas_bg.width / dpr - config.width) {
point_x = canvas_bg.width / dpr - config.width;
}
window.requestAnimationFrame(moveDraw)
}
}
function end(e) {
ifMove = false; }}function moveDraw() {
let now_x = point_x;
// Whether to move the scale
if (config.openUnitChange) {
const st = ( config.start / config.capacity - Math.floor(config.start / config.capacity)) * config.unit;
now_x = Math.round(this.point_x / config.unit) * config.unit - st;
}
ctx.clearRect(0.0, config.width, config.height);
// ctx.drawImage(canvas_bg, now * dpr, 0, config.width * dpr, config.height * dpr, 0, 0, config.width, config.height);
var imageData = ctx_bg.getImageData(now_x * dpr, 0, config.width * dpr, config.height * dpr)
ctx.putImageData(imageData, 0.0)
drawMidLine();
drawSign();
const value = now_x * config.capacity / config.unit + config.start;
if (typeof callBack === 'function') {
callBack(Math.round(value));
} else {
throw new Error(The second argument to the 'scale 'function must be the correct callback function! ')}}Copy the code
Smooth movement, easing the function
The above event interaction is the code for the first scenario. Smoothing is not performed. On this basis, change to the code of the second scheme, and add smooth movement, slow function using easeOut, first after slow.
// easeOut easeOut
const slowActionfn = function (t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
};
// Event interaction
function addEvent() {
let begin_x = 0; // Finger x coordinate
let last_x = 0; // The last x coordinate
let ifMove = false; // Whether to start the interaction
let from_def = 0;
let lastMoveTime = 0;
let lastMove_x = 0;
// Register events, mobile and PC
const hasTouch = 'ontouchstart' in window;
const startEvent = hasTouch ? 'touchstart' : 'mousedown';
const moveEvent = hasTouch ? 'touchmove' : 'mousemove';
const endEvent = hasTouch ? 'touchend' : 'mouseup';
canvas.addEventListener(startEvent, start);
canvas.addEventListener(moveEvent, move);
canvas.addEventListener(endEvent, end);
function start(e) {
e.stopPropagation();
e.preventDefault();
ifMove = true;
if(! e.touches) { last_x = begin_x = e.pageX; }else {
last_x = begin_x = e.touches[0].pageX;
}
lastMove_x = last_x;
lastMoveTime = e.timeStamp || Date.now();
}
function move(e) {
e.stopPropagation();
e.preventDefault();
const current_x = e.touches ? e.touches[0].pageX : e.pageX;
if (ifMove) {
move_x = current_x - last_x;
current_def = current_def - move_x * (config.capacity / config.unit);
window.requestAnimationFrame(moveDraw);
last_x = current_x;
const nowTime = e.timeStamp || Date.now();
if (nowTime - lastMoveTime > 300) { lastMoveTime = nowTime; lastMove_x = last_x; }}}function end(e) {
const current_x = e.changedTouches ? e.changedTouches[0].pageX : e.pageX;
const nowTime = e.timeStamp || Date.now();
const v = -(current_x - lastMove_x) / (nowTime - lastMoveTime); // The last period of time the finger stroke speed
ifMove = false;
let t = 0, d = 15;
if (Math.abs(v) >= 0.3) {
from_def = current_def;
step();
} else {
if (current_def < config.start) {
current_def = config.start;
} else if (current_def > config.end) {
current_def = config.end;
}
if (config.openUnitChange) {
current_def = Math.round(current_def / config.capacity) * config.capacity;
}
moveDraw();
}
function step() {
current_def = slowActionfn(t, from_def, (config.capacity) * v * 50, d);
if (current_def < config.start) {
current_def = config.start;
} else if (current_def > config.end) {
current_def = config.end;
}
if (config.openUnitChange) {
current_def = Math.round(current_def / config.capacity) * config.capacity;
}
moveDraw()
t++;
if (t <= d) {
// Keep moving
window.requestAnimationFrame(step);
} else {
/ / end}}}}function moveDraw() {
ctx.clearRect(0.0, config.width, config.height);
drawScale();
drawMidLine();
drawSign();
if (typeof callBack === 'function') {
callBack(Math.round(current_def));
} else {
throw new Error(The second argument to the 'scale 'function must be the correct callback function! ')}}Copy the code
Boundary processing
In the drawing process, there are various boundary cases that need to be dealt with:
- Simple validation prompts for incoming configuration items, such as whether the ID is a valid DOM node;
- Calculates and sets the default centerline position;
- When sliding, the critical value processing when sliding to the left and right;
- Processing when the incoming scale value is not within the scale interval;
These boundary cases are simply handled in the component library code.
Q&A
For more details, please check out my other post – “Record Canvas Usage and FAQ”
Drawing canvas on mobile terminal is fuzzy
The phenomenon of
The diagram below:
www.yuque.com/nowthen/lon…
why
Regarding mobile hd screen DPR, picture blur, mobile terminal adaptation and other issues, unclear children’s shoes can refer to “About mobile terminal adaptation, you must know” this article, more detailed. Without further elaboration, this article will only deal with Canvas ambiguity on mobile. On the hd screen of mobile terminal, the problem of Canvas blur is often encountered. It’s essentially the same as the blur problem on mobile. The image drawn by Canvas is also a bitmap. On the screen with DPR > 1, one pixel of the bitmap may be rendered by multiple physical pixels. However, these physical pixels cannot be accurately assigned the color corresponding to the bitmap pixel, and only approximate values can be taken, so the screen with DPR > 1 will be blurred. When drawing canvas graphics on THE PC side, we directly treat 1 Canvas pixel as 1px CSS pixel. This is not a problem. The DPR of PC side screen should be 1. This can’t be done directly on mobile screens with DPR > 1.
To solve
The solution, of course, starts with DPR.
- through
window.devicePixelRatio
Get the DPR of the current device screen; - First get or set the width and height of the Canvas container;
- Set the width and height attributes of the Canvas element according to DPR; in
dpr = 2
Is equivalent to expanding the canvas 2 times; - through
context.scale(dpr, dpr)
Scale the frame of the Canvas. indpr = 2
In this way, the drawing scale is enlarged by 2 times. Then the actual drawing pixel of canvas can be processed according to the original pixel value.
When rendered to the screen, the enlarged canvas graphic is rendered to the Canvas container in equal scale. To ensure the quality of canvas graphics.
/ / get the DPR
const dpr = window.devicePixelRatio;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Get the width and height of the Canvas container
const { width: cssWidth, height: cssHeight } = canvas.getBoundingClientRect();
// Set Canvas width and height according to DPR, so that 1 Canvas pixel is equal to 1 physical pixel
canvas.width = dpr * cssWidth;
canvas.height = dpr * cssHeight;
// Set the canvas element width and height attributes according to DPR
ctx.scale(dpr,dpr);
Copy the code
Canvas drawImage() parameter problem, mobile image blur problem
Canvas’s drawImage() function has a particularly confusing aspect. Its 5 and 9 arguments are used in different argument positions. Not paying attention to this in actual development can leave you particularly confused as to what the problem is! Sweat!
One of the quirks of the drawImage() method is that the position of the parameters is different between the 5 and 9 arguments. This is different from the normal API. Generally API optional parameters are placed behind. However, when drawImage() uses nine arguments, the optional arguments sx, sy, sWidth and sHeight are first. If you don’t pay attention to this, some behaviors will be incomprehensible.
And the image inserted by the drawImage() function will also have the problem of image blur on the mobile DPR >1 screen. When the mobile terminal loads another drawn Canvas element through drawImage(), it should also pay attention to the compatibility processing of the other Canvas element and the difference of coordinate system between the two.
// Set the width and height of canvas_bg
canvas_bg.width = (config.unit * (scale_len - 1) + config.width) * dpr; canvas_bg.height = config.height * dpr; ctx_bg.scale(dpr, dpr); .// Initialize the start position
point_x = (config.def - config.start) / config.capacity * config.unit;
// On the main canvas CTX, insert another canvas_bg canvas by drawImage();
ctx.drawImage(canvas_bg, point_x * dpr, 0, config.width * dpr, config.height * dpr, 0.0, config.width, config.height);
Copy the code
In the above code, the canvas_bg canvas also needs to deal with the canvas blur problem mentioned above; On the main canvas CTX, when another canvas_bg canvas graph is inserted by drawImage(), it is necessary to pay attention to the difference in the proportion of coordinate system between the two. At this time, the coordinate system of Canvas_bg is scaled according to DPR.
When the size of Canvas drawing size or drawImage inserting image or getImageDate obtaining graphic resources is larger than a certain threshold, the problem of drawing blank may occur.
In the actual development, when the size of Canvas drawing size or drawImage inserting image and getImageDate obtaining graphics resource is greater than a certain threshold, the rendered picture is blank. The exact threshold is uncertain, depending on the runtime environment. But this should also be a potential flare for drawImage drawing. For example, in the picture below, the scale canvas drawn is too large, and it is cut and rendered on the main canvas. The whole scale is blank, but the interaction is not affected.
Component library encapsulation
Above is the development idea and the main code. The next step is to wrap it as an open source component library file that can be imported directly into your project. The first step is to modify the code appropriately, encapsulating it into classes and building it as an instance of action. To improve code reuse and avoid interplay between multiple introductions and uses. The final code structure:
For simplicity, build component libraries directly using the build library pattern provided by Vue-CLI3. The built library outputs versions of the CommonJS package, UMD package, and so on. When using the corresponding file.
The Canvas – Scale component library has been published to NPM. NPM address: www.npmjs.com/package/can…
Project link:
Github source address: github.com/now1then/ca… ; Articles – language finch: www.yuque.com/nowthen/lon… ;
Another article covered – “Documenting Canvas Usage and FAQ”
Demo: rnlvwyx.cn:3333/#/demo; (Related configuration items to be added…)
Questions are welcome…