Merge branch 'konvajs:master' into master

This commit is contained in:
iaosee 2025-03-27 17:28:40 +08:00 committed by GitHub
commit f0a60496e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 90 additions and 60 deletions

View File

@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## 9.3.20 (2025-03-20)
- Fix text rendering when ellipses are used
## 9.3.19 (2025-03-12)
- Typescript fixes
- Memory leak fixes
## 9.3.18 (2024-12-23) ## 9.3.18 (2024-12-23)
- Fixed emoji split in multiple lines - Fixed emoji split in multiple lines

View File

@ -1,6 +1,6 @@
{ {
"name": "konva", "name": "konva",
"version": "9.3.18", "version": "9.3.20",
"description": "HTML5 2d canvas library.", "description": "HTML5 2d canvas library.",
"author": "Anton Lavrenov", "author": "Anton Lavrenov",
"files": [ "files": [

View File

@ -49,7 +49,7 @@ echo "build for $1"
npm run build >/dev/null npm run build >/dev/null
git commit -am "build for $1" --allow-empty >/dev/null git commit -am "build for $1" --allow-empty >/dev/null
echo "update CDN link in REAME" echo "update CDN link in README"
perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md >/dev/null perl -i -pe "s|${old_cdn_min}|${new_cdn_min}|g" ./README.md >/dev/null
git commit -am "update cdn link" --allow-empty >/dev/null git commit -am "update cdn link" --allow-empty >/dev/null
@ -61,17 +61,17 @@ git push >/dev/null
git push --tags >/dev/null git push --tags >/dev/null
npm publish npm publish
echo "copy konva.js into konva-site" # echo "copy konva.js into konva-site"
cp ./konva.js ../konva-site/ # cp ./konva.js ../konva-site/
cd ../konva-site # cd ../konva-site
echo "replace CDN links" # echo "replace CDN links"
find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.json" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null # find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.json" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null
find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.html" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null # find source themes/hexo3/layout react-demos vue-demos main-demo -name "*.html" -exec perl -i -pe "s|${old_version}|${new_version}|g" {} + >/dev/null
echo "regenerate site" # echo "regenerate site"
./deploy.sh >/dev/null # ./deploy.sh >/dev/null
echo "DONE!" echo "DONE!"

View File

@ -1,8 +1,6 @@
// import resolve from 'rollup-plugin-node-resolve'; // import resolve from 'rollup-plugin-node-resolve';
import typescript from 'rollup-plugin-typescript2'; import typescript from 'rollup-plugin-typescript2';
const pkg = require('./package.json');
export default { export default {
input: `src/index.ts`, input: `src/index.ts`,
output: [ output: [

View File

@ -11,7 +11,7 @@ import { _registerNode } from './Global';
* @constructor * @constructor
* @memberof Konva * @memberof Konva
* @augments Konva.Layer * @augments Konva.Layer
* @@containerParams @@containerParams
* @example * @example
* var layer = new Konva.FastLayer(); * var layer = new Konva.FastLayer();
*/ */

View File

@ -40,9 +40,16 @@ export interface ImageConfig extends ShapeConfig {
* imageObj.src = '/path/to/image.jpg' * imageObj.src = '/path/to/image.jpg'
*/ */
export class Image extends Shape<ImageConfig> { export class Image extends Shape<ImageConfig> {
private _loadListener: () => void;
constructor(attrs: ImageConfig) { constructor(attrs: ImageConfig) {
super(attrs); super(attrs);
this.on('imageChange.konva', () => { this._loadListener = () => {
this._requestDraw();
};
this.on('imageChange.konva', (props: any) => {
this._removeImageLoad(props.oldVal);
this._setImageLoad(); this._setImageLoad();
}); });
@ -59,11 +66,19 @@ export class Image extends Shape<ImageConfig> {
return; return;
} }
if (image && image['addEventListener']) { if (image && image['addEventListener']) {
image['addEventListener']('load', () => { image['addEventListener']('load', this._loadListener);
this._requestDraw();
});
} }
} }
_removeImageLoad(image: any) {
if (image && image['removeEventListener']) {
image['removeEventListener']('load', this._loadListener);
}
}
destroy() {
this._removeImageLoad(this.image());
super.destroy();
return this;
}
_useBufferCanvas() { _useBufferCanvas() {
const hasCornerRadius = !!this.cornerRadius(); const hasCornerRadius = !!this.cornerRadius();
const hasShadow = this.hasShadow(); const hasShadow = this.hasShadow();

View File

@ -534,12 +534,23 @@ export class Text extends Shape<TextConfig> {
// Convert array indices to string // Convert array indices to string
lineArray = stringToArray(line), lineArray = stringToArray(line),
substr = lineArray.slice(0, mid + 1).join(''), substr = lineArray.slice(0, mid + 1).join(''),
substrWidth = this._getTextWidth(substr) + additionalWidth; substrWidth = this._getTextWidth(substr);
if (substrWidth <= maxWidth) { // Only add ellipsis width when we need to consider truncation
// for the current line (when it might be the last visible line)
const shouldConsiderEllipsis =
shouldAddEllipsis &&
fixedHeight &&
currentHeightPx + lineHeightPx > maxHeightPx;
const effectiveWidth = shouldConsiderEllipsis
? substrWidth + additionalWidth
: substrWidth;
if (effectiveWidth <= maxWidth) {
low = mid + 1; low = mid + 1;
match = substr; match = substr;
matchWidth = substrWidth; matchWidth = substrWidth; // Store actual text width without ellipsis
} else { } else {
high = mid; high = mid;
} }

View File

@ -17,25 +17,9 @@
padding: 0; padding: 0;
margin: 0; margin: 0;
} }
.test {
position: absolute;
color: red;
font-size: 20px;
font-family: Arial;
border: 0;
background-color: transparent;
outline: none;
resize: none;
overflow: hidden;
line-height: 1;
padding: 0px;
letter-spacing: 20px;
width: 500px;
text-align: center;
}
</style> </style>
<!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> --> <!-- <script src="https://cdn.rawgit.com/hammerjs/touchemulator/master/touch-emulator.js"></script> -->
<script src="https://unpkg.com/gifler@0.1.0/gifler.min.js"></script>
<script> <script>
// TouchEmulator(); // TouchEmulator();
</script> </script>
@ -52,36 +36,49 @@
<script type="module"> <script type="module">
import Konva from '../src/index.ts'; import Konva from '../src/index.ts';
var stageWidth = window.innerWidth; var width = window.innerWidth;
var stageHeight = window.innerHeight; var height = window.innerHeight;
Konva._fixTextRendering = true;
var stage = new Konva.Stage({ var stage = new Konva.Stage({
container: 'container', container: 'container',
width: stageWidth, width: width,
height: stageHeight, height: height,
}); });
Konva._fixTextRendering = true; var layer = new Konva.Layer();
const layer = new Konva.Layer();
stage.add(layer); stage.add(layer);
const shape = new Konva.Text({ var canvas = document.createElement('canvas');
x: 50, // use external library to parse and draw gif animation
y: 50, function onDrawFrame(ctx, frame) {
text: 'Hello', // update canvas size
fontSize: 20, canvas.width = frame.width;
fontFamily: 'Arial', canvas.height = frame.height;
letterSpacing: 20, // update canvas that we are using for Konva.Image
align: 'center', ctx.drawImage(frame.buffer, 0, 0);
width: 500, // redraw the layer
}); layer.draw();
layer.add(shape); }
text.style.top = shape.y() + 'px'; gifler('https://konvajs.org/assets/yoda.gif').frames(canvas, onDrawFrame);
text.style.left = shape.x() + 'px';
function testKonvaImage() {
setInterval(() => {
const image = new Konva.Image({
image: canvas,
x: Math.random() * width,
y: Math.random() * height,
});
layer.add(image);
setTimeout(() => {
image.image(canvas);
image.destroy();
}, 500);
}, 10);
}
testKonvaImage();
</script> </script>
</body> </body>
</html> </html>

View File

@ -572,7 +572,7 @@ describe('Text', function () {
if (isBrowser) { if (isBrowser) {
assert.equal( assert.equal(
layer.getContext().getTrace(false, true), layer.getContext().getTrace(false, true),
"clearRect(0,0,578,200);save();transform(1,0,0,1,10,10);font=normal normal 14px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(HEADING,18,7);restore();save();fillStyle=black;fillText(,50,21);restore();save();fillStyle=black;fillText(All the world's,7,35);restore();save();fillStyle=black;fillText(a stage,,25,49);restore();save();fillStyle=black;fillText(merely,28,63);restore();save();fillStyle=black;fillText(players. They,7,77);restore();save();fillStyle=black;fillText(have…,27,91);restore();restore();" "clearRect(0,0,578,200);save();transform(1,0,0,1,10,10);font=normal normal 14px Arial;textBaseline=middle;textAlign=left;translate(0,0);save();fillStyle=black;fillText(HEADING,18,7);restore();save();fillStyle=black;fillText(,50,21);restore();save();fillStyle=black;fillText(All the world's a,1,35);restore();save();fillStyle=black;fillText(stage, merely,7,49);restore();save();fillStyle=black;fillText(players. They,7,63);restore();save();fillStyle=black;fillText(have theirrrrrrr,5,77);restore();save();fillStyle=black;fillText(exits and…,14,91);restore();restore();"
); );
} }
}); });