Маленький брат~ научу тебя рисовать хорошую спортивную машину

JavaScript
Маленький брат~ научу тебя рисовать хорошую спортивную машину

предисловие

Вдохновение пришло от денег, которые я собрал несколько дней назад, и я подумал, что пришло время купить себе машину.Поработав столько лет, я должен относиться к себе лучше.Я искал в Интернете и увидел эту модель. На самом деле, я купил один несколько лет назад, но его украли менее чем за месяц, что надолго меня огорчило. На этот раз он должен быть заперт, а три замка гарантируют, что вор не сможет снова украсть его, поэтому я взял деньги и купил немногоЯкульт, делюсь с коллегами, и настроение пока относительно радостное. ——@IT · Союз братьев Пинтоу首席填坑官Сунан(Юг·Вс) ^_^~

Но если ты хочешь быть прогой (ну еще новичком, профессиональным старшиной по засыпке ям 😇), то машина в принципе бесполезна, зачем? Поскольку есть ошибки, которые невозможно исправить, я помню, что моей самой большой мечтой во время выпуска было: «Подобрать девушку и объединиться, чтобы бороться с миром, наказывать зло, грабить богатых и помогать бедным, и наслаждайся жизнью~", но когда я вошла в ЭТО, это было как море,Ошибка не может быть исправлена ​​отныне.. Итак, давайте узнаем больше, просто научитесь рисовать машину, чтобы удовлетворить свой собственный комфорт, и поделитесь этим со всеми здесь. Хорошо, это небольшое недоразумение~ Давайте сначала взглянем на окончательные рендеры!

每周动画一点点之canvas自行车的绘制,图片来源于Google搜索

Анализ процесса:

Эффект есть, самочувствие хорошее? ? На самом деле, это просто нормально~, позвольте мне вместе с вами разобрать процесс его реализации. ​canvasКитайское имя:холст,это как рисовать на бумаге.Прежде чем что-то рисовать,надо сначала научиться придумывать и разбирать то,что хочешь нарисовать.Так же как автомобили,мобильные телефоны и т.д.,готовый продукт состоит из многих частей.,когда ты разобрать, немного доделать и потом собрать, станет намного легче.

  • нарисовать горизонт:
    • Сначала берем определенное соотношение исходя из высоты полотна и проводим линию внизу;
    • Судя по наблюдению за анимацией, у нее все еще есть несколько точек.Это используется для прокрутки параллакса, чтобы обмануть наши глаза.Непосредственно перемещать линию определенно бесполезно.Движение точек может создать эффект анимации;
    • С небольшой модификацией движение нескольких точек становится слишком монотонным.Вы можете себе представить, что когда вы едете на велосипеде,车的速度Есть пересечение с окружающими вещами, зданиями и людьми. Это ощущение очень возбуждает, поэтому давайте добавим что-нибудь, чтобы анимация выглядела богаче. Я выбрал три линии, а сами линии имеют эффект градиентного перехода. , анимация выглядит более реалистичен, чем сплошной цвет, и изначально он не входит в рамки холста, поэтому обратите внимание на этот момент;
    • Следующие две картинки, вторая вырезана в инструменте для генерации gif, это декомпозиция анимации, по сути所谓的动画, который тоже состоит из статичных картинок, а затем быстро переходит в форму параллакса зрения и окончательно обманывает мозг, я увидел анимацию...
    • Точка знаний:lineTo,strokeStyle,stroke,restoreПодождите, я не буду объяснять их здесь по порядку, если вы не понимаете, вы можете сделать это самиПосмотреть API w3school,

本文由@IT·平头哥联盟-首席填坑官∙苏南分享 本文由@IT·平头哥联盟-首席填坑官∙苏南分享,每周动画一点点之canvas自行车的绘制

  horizon(){
      /**
      * 轮子的底部,也称地平线:
      1.清除画布
      2.画一条直线,且高度6px
      本文@IT·平头哥联盟-首席填坑官∙苏南分享,非商业转载请注明原链接及出处
       */

      this.wheelPos = [];
      this.ctx.save();
      this.ctx.clearRect(0, 0, this.canvasW, this.canvasH);

      let horizonX = 0,horizonY = this.canvasH-100;
      this.ctx.beginPath();
      this.ctx.strokeStyle = this.color;
      this.ctx.lineWidth=6;
      this.ctx.moveTo(horizonX,horizonY);
      this.ctx.lineTo(this.canvasW,horizonY);
      this.ctx.closePath();
      this.ctx.stroke();

      Array.from({length:5}).map((k,v)=>{
          let dotProportion = (this.canvasW*0.49)*v-this.oneCent;
          this.wheelPos.push({x:dotProportion,y:horizonY-this.wheelRadius});
          let startX = dotProportion-(this.animateNum*2); //用于动画滚动移动
          this.ctx.beginPath();
          this.ctx.strokeStyle = "#f9f8ef";
          this.ctx.lineWidth=6;
          this.ctx.moveTo(startX,horizonY);
          this.ctx.lineTo(startX+5,horizonY);
          this.ctx.closePath();
          this.ctx.stroke();
      });
      this.ctx.restore();
      this.shuttle();
      // this.wheel();
  }
  shuttle(){
      /**
      * 画几根横线,有点视差,感觉骑车在飞速穿梭的感觉:
      本文@IT·平头哥联盟-首席填坑官∙苏南分享,非商业转载请注明原链接及出处
       */
      let shuttleX = this.canvasW+100,
              shuttleY = this.canvasH/6;
      let shuttleW = shuttleX+100;
      [0,40,0].map((k,v)=>{
          let random = Math.random()+2;
          let x = shuttleX+k-(this.animateNum*(2.2*random));
          let y = shuttleY+v*24;
          let w = shuttleW+k-(this.animateNum*(2.2*random));
          let grd=this.ctx.createLinearGradient(x,y,w,y);
          grd.addColorStop(0,"#30212c");
          grd.addColorStop(1,"#fff");
          this.ctx.beginPath();
          this.ctx.lineCap="round";
          this.ctx.strokeStyle = grd;
          this.ctx.lineWidth=3;
          this.ctx.moveTo(x,y);
          this.ctx.lineTo(w,y);
          this.ctx.stroke();
          this.ctx.closePath();

      });

  }
  • рисовать колесо:

    • Далее нарисуем два колеса автомобиля Где колеса? Я также некоторое время наблюдал за этим и обнаружил, что только что горизонт, положение двух точек, является центральной точкой колеса;
    • Так вот, когда точки только рисовались, были записаны координаты 5 точек, что спасло один расчет, а посередине их было две, которые нам были нужны.
    • Точка знаний:arc,fill

每周动画一点点之canvas自行车的车轮

  console.log(this.wheelPos);
  this.wheelPos = this.wheelPos.slice(1,3); //这里取1-3
  console.log(this.wheelPos);
  this.wheelPos.map((wheelItem,v)=>{
    let wheelItemX = wheelItem.x, 
    wheelItemY= wheelItem.y-this.wheelBorder/1.5;

    //外胎
    this.ctx.beginPath();
    this.ctx.lineWidth=this.wheelBorder;
    this.ctx.fillStyle = "#f5f5f0";
    this.ctx.strokeStyle = this.color;
    this.ctx.arc(wheelItemX,wheelItemY,this.wheelRadius,0,Math.PI*2,false);
    this.ctx.closePath();
    this.ctx.stroke();
    this.ctx.fill();


    //最后两轮胎中心点圆轴承
    this.axisDot(wheelItemX,wheelItemY);
    this.ctx.restore();
    
  });
  this.ctx.restore();
  • Таким же образом сверху нарисованы две окружности, но колеса должны быть с подшипниками.Я сделал какую-то обработку на передних и задних колесах, а задние колеса сплошные с прокладкой;
  • Переднее колесо представляет собой круг с точкой останова, нарисованной для вращения анимации,
  • Сделайте определенное сравнение по радиусу внешнего колеса, нарисуйте внутреннюю окружность, здесь я беру 0,94 внешней окружности за радиус внутренней окружности,
  • Также добавлены два штриха полукруга, чтобы во время анимации колеса чувствовали себя движущимися Полукруг Math.PI равен 180, (Math.PI * градусы) / 180, градусы — это то, что мы хотим нарисовать начальным/конечным углом;
  • Как вы можете видеть на изображении ниже, круг заполнен радиоактивным градиентом.createRadialGradient- Создание радиальных/круговых градиентов (используется в содержимом холста)

每周动画一点点之canvas自行车的车轮

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

  context.createRadialGradient(x0,y0,r0,x1,y1,r1);
  + createRadialGradient API 说明:
    x0 = 渐变的开始圆的 x 坐标
    y0 = 渐变的开始圆的 y 坐标
    r0 = 开始圆的半径
    x1 = 渐变的结束圆的 x 坐标
    y1 = 渐变的结束圆的 y 坐标
    r1 = 结束圆的半径

    详细使用请看下面代码的实例

  let scaleMultiple = this.wheelRadius*.94;
  let speed1 = this.animateNum*2; //外圈半圆速度
  let speed2 = this.animateNum*3; //内小圈半圆速度
  //后轮
  if(v === 0){
    
    //内圆
    this.ctx.beginPath();
    let circleGrd=this.ctx.createRadialGradient(wheelItemX,wheelItemY,18,wheelItemX,wheelItemY,scaleMultiple);
      circleGrd.addColorStop(0,"#584a51");
      circleGrd.addColorStop(1,"#11090d");
    this.ctx.fillStyle = circleGrd;
    this.ctx.arc(wheelItemX,wheelItemY,scaleMultiple,0,Math.PI*2,false);
    this.ctx.fill();
    this.ctx.closePath();

    //两个半圆线
    
    [
      {lineW:2,radius:scaleMultiple*.6,sAngle:getRads(-135+speed1) , eAngle:getRads(110+speed1)},
      {lineW:1.2,radius:scaleMultiple*.45,sAngle:getRads(45+speed2) , eAngle:getRads(-50+speed2)}
    ].map((k,v)=>{
      this.ctx.beginPath();
      this.ctx.lineCap="round";
      this.ctx.strokeStyle ="#fff";
      this.ctx.lineWidth=k.lineW;
      this.ctx.arc(wheelItemX,wheelItemY,k.radius,k.sAngle,k.eAngle,true);
      this.ctx.stroke();
      this.ctx.closePath();

    });
    this.ctx.restore();

  }

  • ** Потяните его вниз, и мы возьмемся за переднее колесо, чтобы открыть нож** :
  • Переднее колесо также рисует несколько полуокружностей, вероятно, начиная с определенного угла, а затем соответственно рисует несколько полуокружностей.Все это радиус с изломом посередине, например: eAngle = [0, 135, 270], sAngle = [ -45, 0, 180]; вы можете нарисовать круг, как показано ниже:

本文由@IT·平头哥联盟-首席填坑官∙苏南分享,每周动画一点点之canvas自行车的车轮动画分解图

每周动画一点点之canvas自行车的车轮动画分解图

  • ** См. следующий код для конкретной реализации **:
  //两个圆,再缩小一圈,画线圆
  Array.from({length:3}).map((k,v)=>{
    let prevIndex = v-1 <= 0 ? 0 : v-1;
    let eAngle = v*135, sAngle = -45+(prevIndex*45)+v*90;
    let radius = scaleMultiple*.75;
    let _color_ = "#120008";
    this.ctx.beginPath();
    this.ctx.lineCap="round";
    this.ctx.strokeStyle = _color_;
    this.ctx.lineWidth=3.5;
    this.ctx.arc(wheelItemX,wheelItemY,radius,getRads(sAngle+speed1),getRads(eAngle+speed1),false);
    this.ctx.stroke();
    this.ctx.closePath();

    if(v<2){
      //再缩小一圈
      let eAngleSmaller = 15+ v*210, sAngleSmaller = -30+v*90;
      let radiusSmaller = scaleMultiple*.45;
      this.ctx.beginPath();
      this.ctx.lineCap="round";
      this.ctx.strokeStyle = _color_;
      this.ctx.lineWidth=3;
      this.ctx.arc(wheelItemX,wheelItemY,radiusSmaller,getRads(sAngleSmaller+speed2),getRads(eAngleSmaller+speed2),false);
      this.ctx.stroke();
      this.ctx.closePath();
    }
    this.ctx.restore();
  });


  • нарисовать каркас тела:

    • Кадр также должен быть одной из самых больших трудностей в этом обмене.Я тоже так думал в начале, но анализ после того, как он стал серьезным, спокойным и тихим, в порядке.
    • В начале это был самый глупый способ.lineTO,moveTo, рисовать линию за линией, на полпути к рисованию я обнаруживаю, что рисую две линии三角или一个菱形Вот и все, а потом перерисовать несколько шпинделей, поэтому я пробовал оба метода,
      • Давайте сначала поговорим о треугольнике и объясним его с помощью рисунка, нарисованного ниже.
      • Найдите центральную точку диска, которая находится выше радиуса заднего колеса;
      • Анализируя структуру кадра, мы можем видеть его как ромб или как два треугольника, здесь мы берем в качестве примера треугольник, ромб можно увидеть в методе carBracket2;
      • Сначала рассчитайте начальную точку треугольника, затем рассчитайте угол и высоту треугольника, см. следующую схему;
      • Наконец, закройте центральную точку заднего колеса точкой, чтобы закрыть часть треугольника.
    • Ромб должен быть проще, но он не кажется таким высокотехнологичным, просто используйтеlineToточечный тире,
    • Выше приведен процесс рисования рамы. На самом деле, я чувствую, что ромбовидная форма проще и меньше кода. Заинтересованные студенты могут попробовать сами. Вы можете увидеть основной код ниже. Для начинающих, если есть лучший Кстати, добро пожаловать в руководство старого водителя:

В заключение:использоватьmoveToПреобразование координат холста изOперейти кAточка х/у,lineToотAначать рисоватьBконец, отBприбытьCточка, замкнутая, т.е. треугольник, завершенный

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

本文由@IT·平头哥联盟-首席填坑官∙苏南分享,每周动画一点点之canvas自行车的车架分解图

本文由@IT·平头哥联盟-首席填坑官∙苏南分享,每周动画一点点之canvas自行车的车架分解图

本文由@IT·平头哥联盟-首席填坑官∙苏南分享


//方法二:三角形
  …………此处省略N行代码
  [
  {
    moveX:triangleX1,
    moveY:triangleY1,
    lineX1:coordinateX,
    lineY1:triangleH1,
    lineX2:discX,
    lineY2:discY,
  },
  {
    moveX:triangleX2+15,
    moveY:triangleY2,
    lineX1:triangleX1,
    lineY1:triangleY1,
    lineX2:discX,
    lineY2:triangleH2,
  },
  ].map((k,v)=>{
    this.ctx.beginPath();
    this.ctx.moveTo(k.moveX,k.moveY); //把坐标移动到A点,从A开始
    this.ctx.strokeStyle = this.gearColor;
    this.ctx.lineWidth=coordinateW;
    this.ctx.lineTo(k.lineX1,k.lineY1);//从A开始,画到B点结束
    this.ctx.lineTo(k.lineX2,k.lineY2); //再从B到C点,闭合
    this.ctx.closePath();
    this.ctx.stroke();
    this.ctx.restore();
  });
  ……

//方法一:菱形
  
  …………此处省略N行代码
  this.ctx.beginPath();
  this.ctx.strokeStyle = this.gearColor;
  this.ctx.lineWidth=coordinateW;
  this.ctx.moveTo(polygon1X,polygon1Y);
  this.ctx.lineTo(coordinateX,height);
  this.ctx.lineTo(discX,discY); 
  this.ctx.lineTo(polygon2X,polygon1Y+5);
  this.ctx.lineTo(polygon2X-5,polygon1Y);
  this.ctx.lineTo(polygon1X,polygon1Y);
  this.ctx.closePath();
  this.ctx.stroke();
  ……

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

  • Рисование роскошного сиденья и подлокотника автомобиля:
    • Сиденье поначалу было довольно запутанным, я не знал, с чего начать, круг не был ни круглым, ни квадратным, поэтому позже я пошел пересматривать API холста и обнаружил, чтоquadraticCurveToЧтобы удовлетворить эту потребность, -Квадратичная кривая Безье
    • После того, как я закончил рисовать, я долго думал об этом, но не нашел никаких навыков или правил.Может быть, у меня не было сил с математикой, поэтому у меня не было другого выбора, кроме как рисовать медленно вот так.
    • Подлокотники одинаковые, начинай пробоватьquadraticCurveTo, у меня долго не получалось рисовать, а потом я пытался найти его соседейbezierCurveTo,—кубическая кривая Безье
    • намекать: кубическая кривая Безье требует трех точек. Первые две точки являются контрольными точками, используемыми в кубическом вычислении Безье, а третья точка является конечной точкой кривой. Начальная точка кривой — это последняя точка текущего пути.
    • Точка знаний:quadraticCurveTo,bezierCurveTo,createLinearGradient

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

  //坐位
  this.ctx.restore();
  let seatX = (discX-85),seatY=discY-140;
  let curve1Cpx = [seatX-5,seatY+30,seatX+75,seatY+8];
  let curve2Cpx =[seatX+85,seatY-5,seatX,seatY]; 
  this.ctx.beginPath();
  // this.ctx.fillStyle = this.gearColor;
  let grd=this.ctx.createLinearGradient(seatX,seatY,seatX+10,seatY+60); //渐变的角度 
  grd.addColorStop(0,"#712450");
  grd.addColorStop(1,"#11090d");
  this.ctx.fillStyle = grd;
  this.ctx.moveTo(seatX,seatY);
  this.ctx.quadraticCurveTo(...curve1Cpx);
  this.ctx.quadraticCurveTo(...curve2Cpx);
  this.ctx.fill();

  //车前轴上的手柄
  let steeringX = lever1X-20,steeringY = lever1Y-45;
  let steeringStep1 = [steeringX+40,steeringY-10,steeringX+40,steeringY-10,steeringX+35,steeringY+15]
  let steeringStep2 = [steeringX+30,steeringY+25,steeringX+25,steeringY+23,steeringX+18,steeringY+23]
  this.ctx.beginPath();
  this.ctx.lineCap="round";
  this.ctx.strokeStyle = "#712450";
  this.ctx.lineWidth=coordinateW;
  this.ctx.moveTo(steeringX,steeringY); //40 60;
  this.ctx.bezierCurveTo(...steeringStep1);
  this.ctx.bezierCurveTo(...steeringStep2);
  this.ctx.stroke();
  this.ctx.closePath();

  • Рисование двигателя и педалей автомобиля:
    • На этом мы подходим к концу этой статьи.Следующее, о чем я хочу рассказать, это самая важная часть транспортного средства, средний зубчатый диск автомобиля. Без него, как бы хорошо вы это ни делали, он будет напрасно;
    • Центральная точка шестерни упоминалась много раз ранее, включая два треугольника, которые представляют собой углы треугольника, вычисляемые из ее центра.Положение легко узнать.Сначала нарисуйте несколько кругов, и каждый из них будет уменьшен в определенной пропорции. ;
    • Затем нарисуйте круг из зазубрин на периферии, чтобы шестерня, вероятно, была нарисована.Фишка шестерни заключается в том, чтобы нарисовать круг с диском в центре.часы тикаютПринцип тот же;
    • Ножная педаль, это легко понять, просто используйтеlineToМожно нарисовать две линии, одна из которых повернута на 90 градусов, но дело в том, что это процесс в процессе анимации Мой процесс анализа выглядит следующим образом:
      • Вертикальный вал опирается на среднюю точку дисковой шестерни.N* (Math.PI / 180)перемена;
      • Горизонтальная ось, то есть ножная педаль, принимает нижнюю часть вертикальной оси за центр оси Y и половину своей ширины за центр оси X.N* (Math.PI / 180)изrotateУгол поворота.
    • Сказав это, давайте взглянем на несколькодинамическое изображениеНу и кстати вставьте код:

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

本文由@IT·平头哥联盟-首席填坑官∙苏南分享


  discGear(coordinateX,coordinateY,coordinateW){
    //车中间齿轮盘 disc
    let discX = coordinateX,discY = coordinateY;
    let discRadius = this.wheelRadius*.36;//车轮的3.6;

    let discDotX = discX+discRadius+8,discDotY = discRadius/.98;
    this.ctx.restore();
    this.ctx.save();
    this.ctx.translate(discX,discY);
    // this.ctx.rotate(-(Math.PI/2));

    Array.from({length:30}).map((v,index)=>{
      let radian = (Math.PI / 15) ;
      this.ctx.beginPath();
      this.ctx.lineCap="round";
      this.ctx.strokeStyle = this.color;
      this.ctx.rotate(radian);
      this.ctx.lineWidth=3;
      this.ctx.moveTo(0,discDotY);
      this.ctx.lineTo(1.5,discDotY);
      // ctx.arc(discDotX,discDotY,6,0,Math.PI*2,false);
      this.ctx.closePath();
      this.ctx.stroke();

    });
    this.pedal(discX,discY,discRadius);
    this.pedal(discX,discY,discRadius,1);
    
    this.ctx.restore();
  }
  pedal(coordinateX,coordinateY,discRadius,turnAngle=0){

    //脚踏板,分两次初始化,一次在中心齿轮绘制之前,一次在之后,

    let pedalX = coordinateX, pedalY = coordinateY - discRadius*.7;
    let pedalW = 6,
        pedalH =  discRadius*1.9;
    let radian = (this.animateNum)*(Math.PI / 180) ;
    let radianHor = (this.animateNum)*(Math.PI / 180) ;
    let turnAngleNum = 1;
    let moveY = 28;
    if(turnAngle !== 0){
      this.ctx.rotate(-180*(Math.PI/180));
      turnAngleNum = (Math.PI/180);
    };
    this.ctx.beginPath();
    this.ctx.rotate(radian*turnAngleNum);
    this.ctx.lineCap="round";
    this.ctx.strokeStyle = this.gearColor;
    this.ctx.lineWidth=pedalW;
    this.ctx.moveTo(-1,moveY);
    this.ctx.lineTo(0,pedalH);
    this.ctx.closePath();
    this.ctx.stroke();
    
    this.ctx.save();
    let pedalHorW = pedalH/1.5,pedalHorH=pedalW;
    this.ctx.translate(0,pedalH);
    this.ctx.beginPath();
    this.ctx.rotate(-radianHor);

    this.ctx.lineCap="round";
    this.ctx.fillStyle = "#fff";
    this.ctx.strokeStyle = this.gearColor;
    this.ctx.lineWidth =2;
    this.ctx.roundRect(-pedalHorW/2,-2,pedalHorW,pedalHorH,5);
    this.ctx.closePath();
    this.ctx.fill();
    this.ctx.stroke();

    this.ctx.restore();
  }

  • нарисовать автомобильную цепь:
    • ЦепьbezierCurveTo, cp1x, cp1y, cp2x, cp2y, x, y и другие параметры нарисованы, смотрите код ниже, по сути это сращивание двух полуэллипсов...

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

  //链条

  let chainW = ( coordinateX+discRadius - this.wheelPos[0].x) / 2;
  let chainX = this.wheelPos[0].x +chainW-5 ;
  let chainY = coordinateY;
  this.ctx.save();
  this.ctx.translate(chainX,chainY+4.8);
  this.ctx.rotate(-2*(Math.PI/180));
  let r = chainW+chainW*.06,h = discRadius/2;
  this.ctx.beginPath();
  this.ctx.moveTo(-r, -1);
  this.ctx.lineWidth=3;
  this.ctx.strokeStyle = "#1e0c1a";
  this.ctx.bezierCurveTo(-r,h*1.5,r,h*4,r,0);
  this.ctx.bezierCurveTo(r,-h*4,-r,-h*1.5,-r,0);
  this.ctx.closePath();
  this.ctx.stroke();
  this.ctx.restore();

конец

Вышеупомянутое сегодня@IT · Союз братьев Пинтоу-首席填坑官СунанСовместное использование принесло вам, весь процесс рисования автомобиля, я чувствую, что должен быть лучший способ для части рамы.Если у вас есть лучшие предложения и идеи, добро пожаловать, чтобы исправить их и, наконец, отправить полный образец изображения! , если вы чувствуете себя хорошо, не забудьте подписаться на нас公众号ой
Получение исходного кода статьи -> блог-ресурс 👈
Хотите просмотреть прямо онлайн 👈

本文由@IT·平头哥联盟-首席填坑官∙苏南分享

宝剑锋从磨砺出,梅花香自苦寒来,做有温度的攻城狮!本文由@IT·平头哥联盟-首席填坑官∙苏南分享,@IT·平头哥联盟 主要分享前端、测试 等领域的积累,文章来源于(自己/群友)工作中积累的经验、填过的坑,希望能尽绵薄之力 助其他同学少走一些弯路

Автор: Сунан-Главный специалист по заполнению карьеров

Данная статья является оригинальной и авторские права принадлежат автору. Для коммерческой перепечатки обращайтесь@IT·平头哥联盟Авторизованная, некоммерческая перепечатка, просьба указывать оригинальную ссылку и источник.