javascript - SVG 失去形状平滑度,点击时改变颜色

标签 javascript svg shapes

通过在 anchor 上单击多次来触发JavaScript函数changeColor(),以应用随机颜色, SVG 形状看起来不平滑,如您所见 ( jsfiddle ):

const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}

const fontSize = 45
const fontBorderWidth = 6

const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))

let pathes = ``
let strokeStartPositions = []
let randomColors = []

function circlesIntersect(circle1, circle2) {
  const deltaX = circle1.x - circle2.x
  const deltaY = circle1.y - circle2.y
  const rSum = circle1.r + circle2.r
  return deltaX*deltaX + deltaY*deltaY <= rSum * rSum
}

function anyCircleIntersects(circleArr) {
  for (const circle1 of circleArr) {
    for (const circle2 of circleArr) {
      if (circle1 !== circle2) {
        if (circlesIntersect(circle1,circle2))
          return true
      }
    }
  }
}

function calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
  const deltaX = p1[0]-p2[0]
  const deltaY = p1[1]-p2[1]
  const distance_p1_to_p2 = Math.sqrt(deltaX*deltaX + deltaY*deltaY)
  const scale = distance_p2_to_p3 / distance_p1_to_p2
  let p3 = []
  p3[0] = p2[0] - deltaX * scale
  p3[1] = p2[1] - deltaY * scale
  return p3
}

let newSvgContent = ``
function getRandomColors(){
  randomColors = [];
  strokeStartPositions = [];
  for (const [i,stroke] of item.strokes.entries()) {
    randomColors.push(randomColor());
    const strokeColor = randomColors[i]
    pathes += `    <path d="${stroke}" fill="${strokeColor}"/>\n`        
    let x = startingPoints[i].x
    x = i<9 ? x-fontSize/4 : x-fontSize/2
    //x = x-fontSize/2
    let y = 900+fontSize/2-startingPoints[i].y
    //y = y-fontSize/2

    strokeStartPositions[i] = [x, y]
  }

  const t0 = Date.now()
  const whileFunction = anyCircleIntersects(strokeStartPositions.map(pos=>{return {r:fontSize/2, x:pos[0], y:pos[1]}}))
  while (whileFunction) {
    if (Date.now() > t0+1000) {
      //pass
      break
    }
    for (const [i,p1] of strokeStartPositions.entries()) {
      const p1X = p1[0]
      const p1Y = p1[1]
      for (const [j,p2] of strokeStartPositions.entries()) {
        if (i === j)
          continue
        const p2X = p2[0]
        const p2Y = p2[1]
        if (p1X === p2X)
          p2[0] = p2[0]+1
        if (p1Y === p2Y)
          p2[1] = p2[1]+1
        const p1Radius = i<9 ? fontSize/2.5 : fontSize/1.75
        const p2Radius = j<9 ? fontSize/2.5 : fontSize/1.75
        if (circlesIntersect({r:p1Radius, x:p1X, y:p1Y}, {r:p2Radius, x:p2X, y:p2Y})) {
          let newP2 = calculate_point_on_other_side_of_p2(p1, p2, 1)
          let newP1 = calculate_point_on_other_side_of_p2(p2, p1, 1)
          strokeStartPositions[i][0] = Math.round(newP1[0])
          strokeStartPositions[i][1] = Math.round(newP1[1])
          strokeStartPositions[j][0] = Math.round(newP2[0])
          strokeStartPositions[j][1] = Math.round(newP2[1])
        }
      }
    }
  }

  let texts = ``
  for (const [i,pos] of strokeStartPositions.entries()) {
    //const textColor = "#FFFFFF"
    const textColor = randomColors[i];
    const x = pos[0]
    const y = pos[1]
    texts += `    <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>\n`
  }

  newSvgContent = `<style type="text/css">
@import url('https://fonts.googleapis.com/css?family=Roboto');
text {
font-family: 'Roboto', sans-serif;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #1D1E22;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} 
</g>
<g>
${texts}
</g>`

  return newSvgContent
}

const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.setAttribute("id", "svg");
svgEl.setAttribute("version", "1.1");
svgEl.setAttribute("viewBox", "0 0 1024 1024");
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
getRandomColors();
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)

//const canvasBbox = svgEl.getBBox()

//const cx = canvasBbox.x + canvasBbox.width/2;
//const cy = canvasBbox.y + canvasBbox.height/2;

//const x = -cx * (0.7 - 1)
//const y = -cy * (0.7 - 1)

////svgEl.style.transform = "scale(0.7)  translate(0px, -"+y+"px)"
//svgEl.style.transform = "scale(0.7)"

function changeColor(){
  var svg = document.getElementById("svg");
  var cloneElement = svg.cloneNode(true);
  getRandomColors();
  cloneElement.innerHTML = newSvgContent
  svg.parentNode.replaceChild(cloneElement, svg);
}

document.getElementById("changeColor").addEventListener("click", function() {
  changeColor();
}, false);
* {
  padding: 0;
  margin: 0;
  border: 0;
}
html, body {
  width: 100%;
  height: 100%;
  background-color: #1D1E22;
  color: #e2e1e0;
}
svg {
  width: 100%;
  height: 100%;
  display: block;
  margin: 0 auto;
}
ul {
  text-align: center;
  list-style: none;
  margin-top: 30px;
}
ul li {
  display: inline-block;
}
ul li:before {
  content: "\07C \020";
  color: #e2e1e0;
}
ul li:last-child:after {
  content: "\020 \07C";
}
a, a:visited, a:focus, a:active {
  text-decoration: none;
  color: #53D633;
  outline: 0;
}
.sliding-middle-out {
  display: inline-block;
  position: relative;
  padding-bottom: 1px;
}
.sliding-middle-out:after {
  content: '';
  display: block;
  margin: auto;
  height: 1px;
  width: 0px;
  background: transparent;

  -webkit-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -moz-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -ms-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -o-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
}
.sliding-middle-out:hover:after {
  width: 100%;
  background: #53D633;
  outline: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.5.2/randomColor.min.js"></script>
<ul>
  <li><a href="#" id="changeColor" title="Change Chinese character colors" class="sliding-middle-out">Change Colors</a></li>
</ul>

如果我更新网页或代码 Playground ,行为会有所不同,如您所见。

我不明白原因,所以一个可能的解决方案,寻找一种在点击时重绘 svg 的方法...

使用的代码替换svg似乎无法解决。

感谢您的帮助

最佳答案

您的问题是,每次单击“更改颜色”按钮时,您都会向 SVG 添加所有路径的重复项。因为您有一堆彼此重叠的相同路径,所以您正在破坏抗锯齿的效果,而抗锯齿效果正是产生平滑边缘的效果。

pathes重置为空字符串是修复错误的一种方法。

但真正的问题是:为什么每次想要更改颜色时都重新生成整个 SVG?只需循环路径和文本元素,然后直接更改 fill 属性即可。

function changeColor(){
  var brushStrokes = document.querySelectorAll("svg g:nth-of-type(1) path");
  var numbers = document.querySelectorAll("svg g:nth-of-type(2) text");
  for (var i=0; i<brushStrokes.length; i++) {
    var color = randomColor();
    brushStrokes[i].setAttribute("fill", color);
    numbers[i].setAttribute("fill", color);
  }
}

const strokeData = {"character":"龥","strokes":["M 128 445 Q 222 525 301 653 Q 319 685 334 709 L 343 725 Q 351 746 367 763 Q 376 772 374 782 Q 374 791 361 801 Q 327 827 298 819 Q 290 819 293 806 Q 310 732 219 604 L 171 539 Q 133 492 33 396 Q 26 392 35 390 Q 43 390 110 431 L 128 445 Z","M 334 709 Q 386 675 447 629 Q 461 617 472 615 Q 478 615 482 624 Q 488 634 474 663 Q 459 700 343 725 C 314 731 309 725 334 709 Z","M 253 553 Q 224 546 246 534 Q 276 517 325 531 Q 423 558 435 563 Q 439 567 439 572 Q 437 586 406 590 Q 388 593 316 566 L 253 553 Z","M 147 441 Q 143 445 128 445 C 101 446 101 446 110 431 Q 125 406 140 365 Q 153 331 162 323 Q 174 311 176 321 Q 178 329 175 343 L 171 363 Q 161 394 153 422 C 148 441 148 441 147 441 Z","M 255 464 Q 233 477 223 476 Q 213 475 180 456 Q 172 453 147 441 C 120 428 124 416 153 422 Q 161 423 181 431 Q 212 442 216 437 Q 221 433 216 383 C 213 353 241 353 245 383 Q 251 431 265 445 C 272 454 272 454 255 464 Z","M 175 343 Q 180 343 186 345 Q 216 357 254 367 Q 261 368 259 373 Q 259 377 245 383 L 216 383 Q 213 383 171 363 C 144 350 145 341 175 343 Z","M 285 468 Q 261 473 255 470 Q 255 468 255 464 C 255 459 255 469 265 445 Q 272 429 278 405 Q 288 371 295 363 Q 305 352 310 361 Q 310 367 310 371 L 308 392 Q 298 434 298 450 C 297 465 297 465 285 468 Z","M 374 410 Q 386 441 396 454 Q 404 463 406 466 C 413 475 413 475 398 484 L 374 498 Q 360 506 347 499 Q 327 482 285 468 C 257 458 270 438 298 450 Q 300 451 310 455 Q 351 468 355 462 Q 357 461 357 459 Q 359 445 342 406 C 330 378 363 382 374 410 Z","M 310 371 Q 323 378 385 390 Q 392 391 392 398 Q 390 402 374 410 C 368 413 368 413 342 406 Q 333 404 308 392 C 281 379 283 358 310 371 Z","M 406 466 Q 424 400 427 396 Q 437 382 441 392 L 443 406 L 441 425 Q 434 470 433 482 C 432 494 432 494 420 500 Q 419 501 416 502 Q 398 508 394 504 Q 390 500 398 484 L 406 466 Z","M 519 439 Q 529 478 541 490 Q 555 506 540 514 Q 514 526 506 528 Q 496 531 484 525 Q 454 510 420 500 C 391 491 403 477 433 482 Q 445 484 454 487 Q 490 496 495 490 Q 496 490 496 487 Q 500 472 490 439 C 482 410 511 410 519 439 Z","M 443 406 Q 449 406 455 407 Q 490 416 527 419 Q 534 420 534 425 Q 534 429 519 439 C 519 439 519 439 490 439 Q 488 441 441 425 C 413 415 413 405 443 406 Z","M 169 267 Q 151 272 139 272 Q 134 270 133 267 Q 132 264 139 246 Q 167 188 145 104 Q 129 91 142 62 Q 151 43 160 37 Q 166 27 173 32 Q 192 50 192 163 Q 192 202 192 242 C 192 261 192 261 169 267 Z","M 347 278 Q 402 290 419 284 Q 433 278 435 258 Q 439 207 429 114 Q 431 98 416 98 Q 396 98 386 100 Q 380 100 377 98 Q 369 94 398 69 Q 419 47 431 22 Q 441 15 451 20 Q 455 22 459 25 Q 480 58 482 158 Q 476 267 486 283 Q 494 292 488 302 Q 484 307 472 314 Q 437 333 419 323 Q 409 319 388 316 Q 271 292 169 267 C 140 260 164 232 192 242 Q 208 248 231 254 L 248 257 Q 272 265 323 274 L 347 278 Z","M 244 188 Q 235 186 214 178 Q 201 174 217 164 Q 226 158 246 163 L 274 170 L 332 184 L 367 190 Q 376 194 396 198 Q 402 199 406 202 Q 415 209 397 218 Q 379 227 368 223 L 332 216 Q 327 216 274 196 L 244 188 Z","M 231 254 Q 231 251 233 246 Q 241 216 244 188 L 246 163 Q 252 102 256 93 Q 265 78 270 87 Q 274 97 274 170 L 274 196 Q 274 208 275 218 Q 276 239 274 243 Q 271 250 248 257 C 229 263 229 263 231 254 Z","M 332 184 Q 332 80 342 71 Q 343 71 347 71 Q 351 73 354 81 Q 360 98 367 190 L 368 223 Q 368 229 372 243 Q 376 259 368 267 Q 355 276 347 278 C 319 288 317 289 323 274 Q 331 257 332 216 L 332 184 Z","M 698 651 L 862 688 Q 908 700 914 708 Q 923 714 918 723 Q 913 729 903 734 Q 864 751 825 733 Q 808 727 794 723 Q 696 689 582 674 Q 548 668 572 653 Q 606 634 659 641 L 698 651 Z","M 657 540 Q 668 553 678 566 Q 699 594 712 608 Q 734 624 698 651 C 674 669 660 671 659 641 Q 659 617 641 563 Q 631 549 630 535 C 626 505 638 517 657 540 Z","M 596 531 Q 594 533 591 535 Q 571 549 557 543 Q 555 541 555 536 Q 555 525 561 515 Q 582 472 572 295 Q 566 269 566 245 Q 566 198 586 180 Q 595 171 604 180 Q 611 192 612 201 L 613 229 Q 615 239 615 265 Q 613 294 613 308 L 613 331 L 613 406 L 613 431 Q 613 455 615 506 C 615 517 615 517 596 531 Z","M 809 223 Q 809 216 817 201 Q 831 173 841 174 Q 855 176 869 209 Q 874 219 876 225 Q 882 242 878 272 Q 868 363 866 451 Q 866 463 868 472 Q 871 489 885 517 Q 892 533 886 539 Q 861 561 817 579 Q 802 583 780 576 Q 774 574 657 540 L 630 535 Q 608 533 596 531 C 566 527 585 503 615 506 Q 627 506 642 510 Q 710 527 768 538 Q 789 542 795 531 Q 809 514 811 453 Q 817 290 811 249 L 809 223 Z","M 613 406 L 618 406 Q 686 417 747 423 Q 768 428 759 440 Q 749 452 727 456 Q 710 459 613 431 C 584 423 583 406 613 406 Z","M 613 308 L 623 308 Q 708 317 761 319 Q 783 323 774 334 Q 762 357 718 354 Q 690 352 613 331 C 584 323 583 308 613 308 Z","M 612 201 L 621 201 L 809 223 C 839 226 835 232 811 249 Q 800 259 777 258 Q 753 257 613 229 C 584 223 582 201 612 201 Z","M 623 161 Q 608 95 455 -29 Q 447 -35 447 -37 Q 446 -41 460 -39 Q 480 -37 504 -27 Q 562 -4 640 76 Q 686 123 690 126 Q 695 131 697 137 Q 704 157 671 167 Q 652 174 641 174 Q 627 174 623 161 Z","M 757 157 Q 757 153 757 151 Q 758 140 774 118 Q 829 47 864 -7 Q 877 -29 892 -36 Q 898 -37 903 -33 Q 923 -23 917 25 Q 914 77 768 163 Q 761 169 757 157 Z"],"medians":[[[306,809],[331,777],[299,694],[261,628],[204,549],[134,471],[36,394]],[[343,709],[352,714],[418,680],[449,658],[470,627]],[[247,544],[296,542],[396,571],[430,571]],[[119,429],[133,427],[170,326]],[[157,429],[195,452],[229,454],[238,443],[232,399],[223,394]],[[180,349],[185,359],[223,372],[254,372]],[[261,467],[274,459],[282,444],[303,366]],[[294,468],[302,463],[319,466],[353,482],[378,469],[365,424],[346,415]],[[312,376],[342,394],[386,397]],[[399,500],[418,476],[436,400]],[[428,499],[438,494],[495,510],[511,504],[517,496],[513,469],[509,453],[497,445]],[[449,411],[455,420],[472,424],[512,429],[528,425]],[[140,265],[165,240],[171,216],[174,151],[162,84],[168,40]],[[176,266],[196,259],[400,303],[434,303],[452,293],[458,266],[459,170],[452,94],[438,71],[383,94]],[[226,173],[325,199],[399,208]],[[236,251],[256,233],[264,91]],[[330,271],[351,252],[345,75]],[[574,665],[607,659],[648,661],[853,714],[907,718]],[[667,636],[685,620],[651,553],[636,541]],[[563,537],[592,497],[590,223],[595,187]],[[601,525],[632,521],[792,558],[815,551],[843,521],[837,466],[846,276],[841,185]],[[616,412],[628,424],[686,434],[729,439],[750,433]],[[618,314],[628,322],[706,335],[741,336],[766,328]],[[617,208],[629,219],[775,239],[792,239],[800,230]],[[680,143],[651,136],[615,87],[554,27],[503,-10],[454,-36]],[[766,154],[877,36],[893,-2],[894,-22]]]}

const fontSize = 45
const fontBorderWidth = 6

const item = strokeData
const charCode = item.character.charCodeAt()
const startingPoints = item.medians.map(i=>({x:i[0][0],y:i[0][1]}))

let pathes = ``
let strokeStartPositions = []
let randomColors = []

function circlesIntersect(circle1, circle2) {
  const deltaX = circle1.x - circle2.x
  const deltaY = circle1.y - circle2.y
  const rSum = circle1.r + circle2.r
  return deltaX*deltaX + deltaY*deltaY <= rSum * rSum
}

function anyCircleIntersects(circleArr) {
  for (const circle1 of circleArr) {
    for (const circle2 of circleArr) {
      if (circle1 !== circle2) {
        if (circlesIntersect(circle1,circle2))
          return true
      }
    }
  }
}

function calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
  const deltaX = p1[0]-p2[0]
  const deltaY = p1[1]-p2[1]
  const distance_p1_to_p2 = Math.sqrt(deltaX*deltaX + deltaY*deltaY)
  const scale = distance_p2_to_p3 / distance_p1_to_p2
  let p3 = []
  p3[0] = p2[0] - deltaX * scale
  p3[1] = p2[1] - deltaY * scale
  return p3
}

let newSvgContent = ``
function getRandomColors(){
  randomColors = [];
  strokeStartPositions = [];
  for (const [i,stroke] of item.strokes.entries()) {
    randomColors.push(randomColor());
    const strokeColor = randomColors[i]
    pathes += `    <path d="${stroke}" fill="${strokeColor}"/>\n`        
    let x = startingPoints[i].x
    x = i<9 ? x-fontSize/4 : x-fontSize/2
    //x = x-fontSize/2
    let y = 900+fontSize/2-startingPoints[i].y
    //y = y-fontSize/2

    strokeStartPositions[i] = [x, y]
  }

  const t0 = Date.now()
  const whileFunction = anyCircleIntersects(strokeStartPositions.map(pos=>{return {r:fontSize/2, x:pos[0], y:pos[1]}}))
  while (whileFunction) {
    if (Date.now() > t0+1000) {
      //pass
      break
    }
    for (const [i,p1] of strokeStartPositions.entries()) {
      const p1X = p1[0]
      const p1Y = p1[1]
      for (const [j,p2] of strokeStartPositions.entries()) {
        if (i === j)
          continue
        const p2X = p2[0]
        const p2Y = p2[1]
        if (p1X === p2X)
          p2[0] = p2[0]+1
        if (p1Y === p2Y)
          p2[1] = p2[1]+1
        const p1Radius = i<9 ? fontSize/2.5 : fontSize/1.75
        const p2Radius = j<9 ? fontSize/2.5 : fontSize/1.75
        if (circlesIntersect({r:p1Radius, x:p1X, y:p1Y}, {r:p2Radius, x:p2X, y:p2Y})) {
          let newP2 = calculate_point_on_other_side_of_p2(p1, p2, 1)
          let newP1 = calculate_point_on_other_side_of_p2(p2, p1, 1)
          strokeStartPositions[i][0] = Math.round(newP1[0])
          strokeStartPositions[i][1] = Math.round(newP1[1])
          strokeStartPositions[j][0] = Math.round(newP2[0])
          strokeStartPositions[j][1] = Math.round(newP2[1])
        }
      }
    }
  }

  let texts = ``
  for (const [i,pos] of strokeStartPositions.entries()) {
    //const textColor = "#FFFFFF"
    const textColor = randomColors[i];
    const x = pos[0]
    const y = pos[1]
    texts += `    <text fill="${textColor}" x="${x}" y="${y}">${i+1}</text>\n`
  }

  newSvgContent = `<style type="text/css">
@import url('https://fonts.googleapis.com/css?family=Roboto');
text {
font-family: 'Roboto', sans-serif;
font-size: ${fontSize}px;
paint-order: stroke;
stroke: #1D1E22;
stroke-width: 6px;
stroke-linecap: butt;
stroke-linejoin: miter;
font-weight: 800;
}
</style>
<g transform="scale(1, -1) translate(0, -900)">
${pathes} 
</g>
<g>
${texts}
</g>`

  return newSvgContent
}

const svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg")
svgEl.setAttribute("id", "svg");
svgEl.setAttribute("version", "1.1");
svgEl.setAttribute("viewBox", "0 0 1024 1024");
svgEl.setAttribute("xmlns", "http://www.w3.org/2000/svg");
getRandomColors();
svgEl.innerHTML = newSvgContent
document.body.appendChild(svgEl)

//const canvasBbox = svgEl.getBBox()

//const cx = canvasBbox.x + canvasBbox.width/2;
//const cy = canvasBbox.y + canvasBbox.height/2;

//const x = -cx * (0.7 - 1)
//const y = -cy * (0.7 - 1)

////svgEl.style.transform = "scale(0.7)  translate(0px, -"+y+"px)"
//svgEl.style.transform = "scale(0.7)"

function changeColor(){
  var brushStrokes = document.querySelectorAll("svg g:nth-of-type(1) path");
  var numbers = document.querySelectorAll("svg g:nth-of-type(2) text");
  for (var i=0; i<brushStrokes.length; i++) {
    var color = randomColor();
    brushStrokes[i].setAttribute("fill", color);
    numbers[i].setAttribute("fill", color);
  }
}

document.getElementById("changeColor").addEventListener("click", function() {
  changeColor();
}, false);
* {
  padding: 0;
  margin: 0;
  border: 0;
}
html, body {
  width: 100%;
  height: 100%;
  background-color: #1D1E22;
  color: #e2e1e0;
}
svg {
  width: 100%;
  height: 100%;
  display: block;
  margin: 0 auto;
}
h1, h2 {
  text-align: center;
}
ul {
  text-align: center;
  list-style: none;
  margin-top: 30px;
}
ul li {
  display: inline-block;
}
ul li:before {
  content: "\07C \020";
  color: #e2e1e0;
}
ul li:last-child:after {
  content: "\020 \07C";
}
a, a:visited, a:focus, a:active {
  text-decoration: none;
  color: #53D633;
  outline: 0;
}
.sliding-middle-out {
  display: inline-block;
  position: relative;
  padding-bottom: 1px;
}
.sliding-middle-out:after {
  content: '';
  display: block;
  margin: auto;
  height: 1px;
  width: 0px;
  background: transparent;

  -webkit-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -moz-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -ms-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  -o-transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
  transition: width 250ms ease-in-out, background-color 250ms ease-in-out;
}
.sliding-middle-out:hover:after {
  width: 100%;
  background: #53D633;
  outline: 0;
}
#mp3:hover {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/randomcolor/0.5.2/randomColor.min.js"></script>
<ul>
  <li><a href="#" id="changeColor" title="Change Chinese character colors" class="sliding-middle-out">Change Colors</a></li>
</ul>

关于javascript - SVG 失去形状平滑度,点击时改变颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52325048/

相关文章:

javascript - 每当您在选择器内移动鼠标时,悬停功能都会重复

jquery - 替换悬停图像后,SVG 图像在 Mac Safari 中以不正确的大小呈现

具有两种不同颜色的Android矩形

javascript - CouchDB 文档映射修改

javascript - 从输入值创建 li 元素

css - 带有单位 px 的 Firefox svg stroke-dasharray

android - 在 Android 上的形状上绘制顶部边框

Android - 如何结合按钮的形状可绘制和文本颜色不同状态?

javascript - (JS) 迭代后从 csv 中删除行

javascript - 使用 jQuery 创建和访问 SVG 标签?